#!/usr/bin/env python3
# Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
import array
import sys
import unittest
from unittest.mock import Mock


class ArrayTest(unittest.TestCase):
    def test_new_with_bad_typecode_raises_value_error(self):
        with self.assertRaises(ValueError) as context:
            array.array.__new__(array.array, "c")
        self.assertEqual(
            str(context.exception),
            "bad typecode (must be b, B, u, h, H, i, I, l, L, q, Q, f or d)",
        )

    def test_new_with_non_str_typecode_raises_type_error(self):
        with self.assertRaises(TypeError) as context:
            array.array.__new__(array.array, b"not-string")
        self.assertIn("must be a unicode character, not bytes", str(context.exception))

    def test_new_with_long_str_typecode_raises_type_error(self):
        with self.assertRaises(TypeError) as context:
            array.array.__new__(array.array, "too")
        self.assertIn("must be a unicode character, not str", str(context.exception))

    def test_new_with_str_array_init_and_non_str_typecode_raises_type_error(self):
        unicode_array = array.array("u")

        with self.assertRaises(TypeError) as context:
            array.array.__new__(array.array, "c", unicode_array)
        self.assertEqual(
            str(context.exception),
            "cannot use a unicode array to initialize an array with typecode 'c'",
        )

    def test_new_with_valid_typecode_returns_array(self):
        byte_array = array.array("b")
        self.assertEqual(byte_array.typecode, "b")

    def test_new_with_array_subtype_returns_subtype(self):
        class ArraySub(array.array):
            pass

        array_sub = array.array.__new__(ArraySub, "B")
        self.assertIsInstance(array_sub, ArraySub)
        self.assertEqual(array_sub.typecode, "B")

    def test_new_with_integer_typecode_and_non_number_raises_type_error(self):
        with self.assertRaises(TypeError) as ctx:
            array.array("i", ["not-a-number"])
        self.assertEqual(str(ctx.exception), "an integer is required (got type str)")

    def test_new_with_integer_typecode_and_float_raises_type_error(self):
        with self.assertRaises(TypeError) as ctx:
            array.array("Q", [2.0])
        self.assertEqual(str(ctx.exception), "array item must be integer")

    def test_new_with_integer_typecode_and_too_large_value_raises_overflow_error(self):
        with self.assertRaises(OverflowError) as ctx:
            array.array("b", [128])
        self.assertIn("greater than maximum", str(ctx.exception))

    def test_new_with_float_typecode_and_non_number_raises_type_error(self):
        with self.assertRaises(TypeError) as ctx:
            array.array("f", ["1"])
        self.assertEqual(str(ctx.exception), "must be real number, not str")

    def test_new_with_list_stores_each_number_in_the_array(self):
        int_array = array.array("i", [1, 2, 3])
        self.assertEqual(int_array[0], 1)
        self.assertEqual(int_array[1], 2)
        self.assertEqual(int_array[2], 3)

    def test_new_with_tuple_stores_each_number_in_the_array(self):
        int_array = array.array("d", (1.0, 2.0, 4.0))
        self.assertEqual(int_array[0], 1.0)
        self.assertEqual(int_array[1], 2.0)
        self.assertEqual(int_array[2], 4.0)

    def test_new_with_iterable_stores_each_number_in_the_array(self):
        class Iterator:
            def __iter__(self):
                return [1, 2, 3].__iter__()

        int_array = array.array("b", Iterator())
        self.assertEqual(int_array[0], 1)
        self.assertEqual(int_array[1], 2)
        self.assertEqual(int_array[2], 3)
        with self.assertRaises(IndexError) as ctx:
            int_array[3]
        self.assertEqual(str(ctx.exception), "array index out of range")

    def test_new_with_float_typecode_and_list_calls_dunder_float_on_elements(self):
        class FloatSub(float):
            pass

        class DunderFloat:
            def __float__(self):
                return 4.0

        float_array = array.array("f", [1.0, FloatSub(2.0), DunderFloat()])
        self.assertEqual(float_array[0], 1.0)
        self.assertEqual(type(float_array[0]), float)
        self.assertEqual(float_array[1], 2.0)
        self.assertEqual(type(float_array[1]), float)
        self.assertEqual(float_array[2], 4.0)
        self.assertEqual(type(float_array[2]), float)

    def test_new_with_integer_typecode_and_list_calls_dunder_index_on_elements(self):
        class IntSub(int):
            pass

        class DunderIndex:
            def __index__(self):
                return 8

        int_array = array.array("i", [1, IntSub(4), DunderIndex()])
        self.assertEqual(int_array[0], 1)
        self.assertEqual(type(int_array[0]), int)
        self.assertEqual(int_array[1], 4)
        self.assertEqual(type(int_array[1]), int)
        self.assertEqual(int_array[2], 8)
        self.assertEqual(type(int_array[2]), int)

    def test_append_does_not_iterate_over_argument(self):
        int_array = array.array("i")
        with self.assertRaises(TypeError) as ctx:
            int_array.append([0, 1])
        self.assertEqual(str(ctx.exception), "an integer is required (got type list)")

    def test_append_grows_array(self):
        int_array = array.array("i")
        self.assertEqual(int_array.__len__(), 0)
        int_array.append(1)
        self.assertEqual(int_array.__len__(), 1)
        int_array.append(2)
        self.assertEqual(int_array.__len__(), 2)
        self.assertEqual(int_array[0], 1)
        self.assertEqual(int_array[1], 2)

    def test_dunder_mul_with_str_raises_type_error(self):
        int_array = array.array("i")
        with self.assertRaises(TypeError) as ctx:
            int_array.__mul__("hello")
        self.assertEqual(
            str(ctx.exception), "'str' object cannot be interpreted as an integer"
        )

    def test_dunder_mul_with_subclass_returns_array(self):
        class ArraySub(array.array):
            pass

        int_array = ArraySub("i", [1, 2, 3])
        new_array = int_array.__mul__(3)
        self.assertEqual(int_array.__len__(), 3)
        self.assertEqual(type(new_array), array.array)
        self.assertEqual(new_array.__len__(), 9)
        self.assertEqual(list(new_array), [1, 2, 3, 1, 2, 3, 1, 2, 3])

    def test_dunder_mul_with_maxsize_raises_memory_error(self):
        int_array = array.array("i", [1, 2, 3])
        with self.assertRaises(MemoryError):
            int_array.__mul__(sys.maxsize)

    def test_dunder_mul_with_int_returns_array(self):
        int_array = array.array("i", [1, 2, 3])
        new_array = int_array.__mul__(3)
        self.assertEqual(int_array.__len__(), 3)
        self.assertEqual(new_array.__len__(), 9)
        self.assertEqual(list(new_array), [1, 2, 3, 1, 2, 3, 1, 2, 3])

    def test_dunder_mul_with_index_returns_array(self):
        class Num:
            def __index__(self):
                return 3

        num = Num()
        int_array = array.array("i", [1, 2, 3])
        new_array = int_array.__mul__(num)
        self.assertEqual(int_array.__len__(), 3)
        self.assertEqual(new_array.__len__(), 9)
        self.assertEqual(list(new_array), [1, 2, 3, 1, 2, 3, 1, 2, 3])

    def test_extend_with_iterable_can_resize_past_length_hint(self):
        class Iterator:
            def __length_hint__(self):
                return 1

            def __iter__(self):
                return [1, 2, 3].__iter__()

        int_array = array.array("b")
        int_array.extend(Iterator())
        self.assertEqual(int_array[0], 1)
        self.assertEqual(int_array[1], 2)
        self.assertEqual(int_array[2], 3)
        with self.assertRaises(IndexError) as ctx:
            int_array[3]
        self.assertEqual(str(ctx.exception), "array index out of range")

    def test_extend_with_iterable_shorter_than_length_hint_resizes_to_iterable(self):
        class Iterator:
            def __length_hint__(self):
                return 6

            def __iter__(self):
                return [1, 2, 3].__iter__()

        int_array = array.array("b")
        int_array.extend(Iterator())
        self.assertEqual(int_array[0], 1)
        self.assertEqual(int_array[1], 2)
        self.assertEqual(int_array[2], 3)
        with self.assertRaises(IndexError) as ctx:
            int_array[3] = 4
        self.assertEqual(str(ctx.exception), "array assignment index out of range")

    def test_subclass_will_call_array_setitem_on_initialization(self):
        class ArraySub(array.array):
            __setitem__ = Mock(name="__setitem__", return_value=None)

        int_array = ArraySub("i", [3, 2, 1])
        self.assertEqual(int_array.__len__(), 3)
        self.assertEqual(int_array[0], 3)
        self.assertEqual(int_array[1], 2)
        self.assertEqual(int_array[2], 1)

        int_array[0] = 0
        int_array.__setitem__.assert_called_once()

    def test_getting_a_value_out_range_raises_index_error(self):
        int_array = array.array("i", [1, 2, 3])
        with self.assertRaises(IndexError) as ctx:
            int_array[5]
        self.assertEqual(str(ctx.exception), "array index out of range")

    def test_getting_a_negative_value_out_of_range_raises_index_error(self):
        int_array = array.array("i", [1, 2, 3])
        with self.assertRaises(IndexError) as ctx:
            int_array[-4]
        self.assertEqual(str(ctx.exception), "array index out of range")

    def test_getting_an_index_longer_than_word_raises_index_error(self):
        int_array = array.array("i", [1, 2, 3])
        with self.assertRaises(IndexError) as ctx:
            int_array[1 << 65]
        self.assertEqual(
            str(ctx.exception), "cannot fit 'int' into an index-sized integer"
        )

    def test_getting_a_non_integer_index_raises_type_error(self):
        int_array = array.array("i", [1, 2, 3])
        with self.assertRaises(TypeError) as ctx:
            int_array["not-int"]
        self.assertIn("integer", str(ctx.exception))

    def test_getitem_wth_dunder_index_calls_dunder_index(self):
        class DunderIndex:
            def __index__(self):
                return 0

        int_array = array.array("i", [1, 2, 3])
        self.assertEqual(int_array[DunderIndex()], 1)

    def test_setting_a_value_out_range_raises_index_error(self):
        int_array = array.array("i", [1, 2, 3])
        with self.assertRaises(IndexError) as ctx:
            int_array[5] = 1
        self.assertEqual(str(ctx.exception), "array assignment index out of range")

    def test_setting_a_negative_value_out_range_raises_index_error(self):
        int_array = array.array("i", [1, 2, 3])
        with self.assertRaises(IndexError) as ctx:
            int_array[-5] = 1
        self.assertEqual(str(ctx.exception), "array assignment index out of range")

    def test_setting_an_index_longer_than_word_raises_index_error(self):
        int_array = array.array("i", [1, 2, 3])
        with self.assertRaises(IndexError) as ctx:
            int_array[1 << 65] = 1
        self.assertEqual(
            str(ctx.exception), "cannot fit 'int' into an index-sized integer"
        )

    def test_setting_a_non_integer_index_raises_type_error(self):
        int_array = array.array("i", [1, 2, 3])
        with self.assertRaises(TypeError) as ctx:
            int_array["not-int"] = 2
        self.assertIn("integer", str(ctx.exception))

    def test_setitem_wth_dunder_index_calls_dunder_index(self):
        class DunderIndex:
            def __index__(self):
                return 0

        int_array = array.array("i", [1, 2, 3])
        int_array[DunderIndex()] = 10
        self.assertEqual(int_array[0], 10)


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

#!/usr/bin/env python3
# Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
import array
import sys
import unittest
from unittest.mock import Mock


class ArrayTest(unittest.TestCase):
    def test_new_with_bad_typecode_raises_value_error(self):
        with self.assertRaises(ValueError) as context:
            array.array.__new__(array.array, "c")
        self.assertEqual(
            str(context.exception),
            "bad typecode (must be b, B, u, h, H, i, I, l, L, q, Q, f or d)",
        )

    def test_new_with_non_str_typecode_raises_type_error(self):
        with self.assertRaises(TypeError) as context:
            array.array.__new__(array.array, b"not-string")
        self.assertIn("must be a unicode character, not bytes", str(context.exception))

    def test_new_with_long_str_typecode_raises_type_error(self):
        with self.assertRaises(TypeError) as context:
            array.array.__new__(array.array, "too")
        self.assertIn("must be a unicode character, not str", str(context.exception))

    def test_new_with_str_array_init_and_non_str_typecode_raises_type_error(self):
        unicode_array = array.array("u")

        with self.assertRaises(TypeError) as context:
            array.array.__new__(array.array, "c", unicode_array)
        self.assertEqual(
            str(context.exception),
            "cannot use a unicode array to initialize an array with typecode 'c'",
        )

    def test_new_with_valid_typecode_returns_array(self):
        byte_array = array.array("b")
        self.assertEqual(byte_array.typecode, "b")

    def test_new_with_array_subtype_returns_subtype(self):
        class ArraySub(array.array):
            pass

        array_sub = array.array.__new__(ArraySub, "B")
        self.assertIsInstance(array_sub, ArraySub)
        self.assertEqual(array_sub.typecode, "B")

    def test_new_with_integer_typecode_and_non_number_raises_type_error(self):
        with self.assertRaises(TypeError) as ctx:
            array.array("i", ["not-a-number"])
        self.assertEqual(str(ctx.exception), "an integer is required (got type str)")

    def test_new_with_integer_typecode_and_float_raises_type_error(self):
        with self.assertRaises(TypeError) as ctx:
            array.array("Q", [2.0])
        self.assertEqual(str(ctx.exception), "array item must be integer")

    def test_new_with_integer_typecode_and_too_large_value_raises_overflow_error(self):
        with self.assertRaises(OverflowError) as ctx:
            array.array("b", [128])
        self.assertIn("greater than maximum", str(ctx.exception))

    def test_new_with_float_typecode_and_non_number_raises_type_error(self):
        with self.assertRaises(TypeError) as ctx:
            array.array("f", ["1"])
        self.assertEqual(str(ctx.exception), "must be real number, not str")

    def test_new_with_list_stores_each_number_in_the_array(self):
        int_array = array.array("i", [1, 2, 3])
        self.assertEqual(int_array[0], 1)
        self.assertEqual(int_array[1], 2)
        self.assertEqual(int_array[2], 3)

    def test_new_with_tuple_stores_each_number_in_the_array(self):
        int_array = array.array("d", (1.0, 2.0, 4.0))
        self.assertEqual(int_array[0], 1.0)
        self.assertEqual(int_array[1], 2.0)
        self.assertEqual(int_array[2], 4.0)

    def test_new_with_iterable_stores_each_number_in_the_array(self):
        class Iterator:
            def __iter__(self):
                return [1, 2, 3].__iter__()

        int_array = array.array("b", Iterator())
        self.assertEqual(int_array[0], 1)
        self.assertEqual(int_array[1], 2)
        self.assertEqual(int_array[2], 3)
        with self.assertRaises(IndexError) as ctx:
            int_array[3]
        self.assertEqual(str(ctx.exception), "array index out of range")

    def test_new_with_float_typecode_and_list_calls_dunder_float_on_elements(self):
        class FloatSub(float):
            pass

        class DunderFloat:
            def __float__(self):
                return 4.0

        float_array = array.array("f", [1.0, FloatSub(2.0), DunderFloat()])
        self.assertEqual(float_array[0], 1.0)
        self.assertEqual(type(float_array[0]), float)
        self.assertEqual(float_array[1], 2.0)
        self.assertEqual(type(float_array[1]), float)
        self.assertEqual(float_array[2], 4.0)
        self.assertEqual(type(float_array[2]), float)

    def test_new_with_integer_typecode_and_list_calls_dunder_index_on_elements(self):
        class IntSub(int):
            pass

        class DunderIndex:
            def __index__(self):
                return 8

        int_array = array.array("i", [1, IntSub(4), DunderIndex()])
        self.assertEqual(int_array[0], 1)
        self.assertEqual(type(int_array[0]), int)
        self.assertEqual(int_array[1], 4)
        self.assertEqual(type(int_array[1]), int)
        self.assertEqual(int_array[2], 8)
        self.assertEqual(type(int_array[2]), int)

    def test_append_does_not_iterate_over_argument(self):
        int_array = array.array("i")
        with self.assertRaises(TypeError) as ctx:
            int_array.append([0, 1])
        self.assertEqual(str(ctx.exception), "an integer is required (got type list)")

    def test_append_grows_array(self):
        int_array = array.array("i")
        self.assertEqual(int_array.__len__(), 0)
        int_array.append(1)
        self.assertEqual(int_array.__len__(), 1)
        int_array.append(2)
        self.assertEqual(int_array.__len__(), 2)
        self.assertEqual(int_array[0], 1)
        self.assertEqual(int_array[1], 2)

    def test_dunder_mul_with_str_raises_type_error(self):
        int_array = array.array("i")
        with self.assertRaises(TypeError) as ctx:
            int_array.__mul__("hello")
        self.assertEqual(
            str(ctx.exception), "'str' object cannot be interpreted as an integer"
        )

    def test_dunder_mul_with_subclass_returns_array(self):
        class ArraySub(array.array):
            pass

        int_array = ArraySub("i", [1, 2, 3])
        new_array = int_array.__mul__(3)
        self.assertEqual(int_array.__len__(), 3)
        self.assertEqual(type(new_array), array.array)
        self.assertEqual(new_array.__len__(), 9)
        self.assertEqual(list(new_array), [1, 2, 3, 1, 2, 3, 1, 2, 3])

    def test_dunder_mul_with_maxsize_raises_memory_error(self):
        int_array = array.array("i", [1, 2, 3])
        with self.assertRaises(MemoryError):
            int_array.__mul__(sys.maxsize)

    def test_dunder_mul_with_int_returns_array(self):
        int_array = array.array("i", [1, 2, 3])
        new_array = int_array.__mul__(3)
        self.assertEqual(int_array.__len__(), 3)
        self.assertEqual(new_array.__len__(), 9)
        self.assertEqual(list(new_array), [1, 2, 3, 1, 2, 3, 1, 2, 3])

    def test_dunder_mul_with_index_returns_array(self):
        class Num:
            def __index__(self):
                return 3

        num = Num()
        int_array = array.array("i", [1, 2, 3])
        new_array = int_array.__mul__(num)
        self.assertEqual(int_array.__len__(), 3)
        self.assertEqual(new_array.__len__(), 9)
        self.assertEqual(list(new_array), [1, 2, 3, 1, 2, 3, 1, 2, 3])

    def test_extend_with_iterable_can_resize_past_length_hint(self):
        class Iterator:
            def __length_hint__(self):
                return 1

            def __iter__(self):
                return [1, 2, 3].__iter__()

        int_array = array.array("b")
        int_array.extend(Iterator())
        self.assertEqual(int_array[0], 1)
        self.assertEqual(int_array[1], 2)
        self.assertEqual(int_array[2], 3)
        with self.assertRaises(IndexError) as ctx:
            int_array[3]
        self.assertEqual(str(ctx.exception), "array index out of range")

    def test_extend_with_iterable_shorter_than_length_hint_resizes_to_iterable(self):
        class Iterator:
            def __length_hint__(self):
                return 6

            def __iter__(self):
                return [1, 2, 3].__iter__()

        int_array = array.array("b")
        int_array.extend(Iterator())
        self.assertEqual(int_array[0], 1)
        self.assertEqual(int_array[1], 2)
        self.assertEqual(int_array[2], 3)
        with self.assertRaises(IndexError) as ctx:
            int_array[3] = 4
        self.assertEqual(str(ctx.exception), "array assignment index out of range")

    def test_subclass_will_call_array_setitem_on_initialization(self):
        class ArraySub(array.array):
            __setitem__ = Mock(name="__setitem__", return_value=None)

        int_array = ArraySub("i", [3, 2, 1])
        self.assertEqual(int_array.__len__(), 3)
        self.assertEqual(int_array[0], 3)
        self.assertEqual(int_array[1], 2)
        self.assertEqual(int_array[2], 1)

        int_array[0] = 0
        int_array.__setitem__.assert_called_once()

    def test_getting_a_value_out_range_raises_index_error(self):
        int_array = array.array("i", [1, 2, 3])
        with self.assertRaises(IndexError) as ctx:
            int_array[5]
        self.assertEqual(str(ctx.exception), "array index out of range")

    def test_getting_a_negative_value_out_of_range_raises_index_error(self):
        int_array = array.array("i", [1, 2, 3])
        with self.assertRaises(IndexError) as ctx:
            int_array[-4]
        self.assertEqual(str(ctx.exception), "array index out of range")

    def test_getting_an_index_longer_than_word_raises_index_error(self):
        int_array = array.array("i", [1, 2, 3])
        with self.assertRaises(IndexError) as ctx:
            int_array[1 << 65]
        self.assertEqual(
            str(ctx.exception), "cannot fit 'int' into an index-sized integer"
        )

    def test_getting_a_non_integer_index_raises_type_error(self):
        int_array = array.array("i", [1, 2, 3])
        with self.assertRaises(TypeError) as ctx:
            int_array["not-int"]
        self.assertIn("integer", str(ctx.exception))

    def test_getitem_wth_dunder_index_calls_dunder_index(self):
        class DunderIndex:
            def __index__(self):
                return 0

        int_array = array.array("i", [1, 2, 3])
        self.assertEqual(int_array[DunderIndex()], 1)

    def test_setting_a_value_out_range_raises_index_error(self):
        int_array = array.array("i", [1, 2, 3])
        with self.assertRaises(IndexError) as ctx:
            int_array[5] = 1
        self.assertEqual(str(ctx.exception), "array assignment index out of range")

    def test_setting_a_negative_value_out_range_raises_index_error(self):
        int_array = array.array("i", [1, 2, 3])
        with self.assertRaises(IndexError) as ctx:
            int_array[-5] = 1
        self.assertEqual(str(ctx.exception), "array assignment index out of range")

    def test_setting_an_index_longer_than_word_raises_index_error(self):
        int_array = array.array("i", [1, 2, 3])
        with self.assertRaises(IndexError) as ctx:
            int_array[1 << 65] = 1
        self.assertEqual(
            str(ctx.exception), "cannot fit 'int' into an index-sized integer"
        )

    def test_setting_a_non_integer_index_raises_type_error(self):
        int_array = array.array("i", [1, 2, 3])
        with self.assertRaises(TypeError) as ctx:
            int_array["not-int"] = 2
        self.assertIn("integer", str(ctx.exception))

    def test_setitem_wth_dunder_index_calls_dunder_index(self):
        class DunderIndex:
            def __index__(self):
                return 0

        int_array = array.array("i", [1, 2, 3])
        int_array[DunderIndex()] = 10
        self.assertEqual(int_array[0], 10)


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

#!/usr/bin/env python3 # Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)