import math
import sys
import unittest
import platform

from pygame import Rect, Vector2
from pygame.tests import test_utils


IS_PYPY = "PyPy" == platform.python_implementation()


class RectTypeTest(unittest.TestCase):
    def _assertCountEqual(self, *args, **kwargs):
        self.assertCountEqual(*args, **kwargs)

    def testConstructionXYWidthHeight(self):
        r = Rect(1, 2, 3, 4)
        self.assertEqual(1, r.left)
        self.assertEqual(2, r.top)
        self.assertEqual(3, r.width)
        self.assertEqual(4, r.height)

    def testConstructionTopLeftSize(self):
        r = Rect((1, 2), (3, 4))
        self.assertEqual(1, r.left)
        self.assertEqual(2, r.top)
        self.assertEqual(3, r.width)
        self.assertEqual(4, r.height)

    def testCalculatedAttributes(self):
        r = Rect(1, 2, 3, 4)

        self.assertEqual(r.left + r.width, r.right)
        self.assertEqual(r.top + r.height, r.bottom)
        self.assertEqual((r.width, r.height), r.size)
        self.assertEqual((r.left, r.top), r.topleft)
        self.assertEqual((r.right, r.top), r.topright)
        self.assertEqual((r.left, r.bottom), r.bottomleft)
        self.assertEqual((r.right, r.bottom), r.bottomright)

        midx = r.left + r.width // 2
        midy = r.top + r.height // 2

        self.assertEqual(midx, r.centerx)
        self.assertEqual(midy, r.centery)
        self.assertEqual((r.centerx, r.centery), r.center)
        self.assertEqual((r.centerx, r.top), r.midtop)
        self.assertEqual((r.centerx, r.bottom), r.midbottom)
        self.assertEqual((r.left, r.centery), r.midleft)
        self.assertEqual((r.right, r.centery), r.midright)

    def test_normalize(self):
        """Ensures normalize works when width and height are both negative."""
        test_rect = Rect((1, 2), (-3, -6))
        expected_normalized_rect = (
            (test_rect.x + test_rect.w, test_rect.y + test_rect.h),
            (-test_rect.w, -test_rect.h),
        )

        test_rect.normalize()

        self.assertEqual(test_rect, expected_normalized_rect)

    @unittest.skipIf(IS_PYPY, "fails on pypy sometimes")
    def test_normalize__positive_height(self):
        """Ensures normalize works with a negative width and a positive height."""
        test_rect = Rect((1, 2), (-3, 6))
        expected_normalized_rect = (
            (test_rect.x + test_rect.w, test_rect.y),
            (-test_rect.w, test_rect.h),
        )

        test_rect.normalize()

        self.assertEqual(test_rect, expected_normalized_rect)

    @unittest.skipIf(IS_PYPY, "fails on pypy sometimes")
    def test_normalize__positive_width(self):
        """Ensures normalize works with a positive width and a negative height."""
        test_rect = Rect((1, 2), (3, -6))
        expected_normalized_rect = (
            (test_rect.x, test_rect.y + test_rect.h),
            (test_rect.w, -test_rect.h),
        )

        test_rect.normalize()

        self.assertEqual(test_rect, expected_normalized_rect)

    @unittest.skipIf(IS_PYPY, "fails on pypy sometimes")
    def test_normalize__zero_height(self):
        """Ensures normalize works with a negative width and a zero height."""
        test_rect = Rect((1, 2), (-3, 0))
        expected_normalized_rect = (
            (test_rect.x + test_rect.w, test_rect.y),
            (-test_rect.w, test_rect.h),
        )

        test_rect.normalize()

        self.assertEqual(test_rect, expected_normalized_rect)

    @unittest.skipIf(IS_PYPY, "fails on pypy sometimes")
    def test_normalize__zero_width(self):
        """Ensures normalize works with a zero width and a negative height."""
        test_rect = Rect((1, 2), (0, -6))
        expected_normalized_rect = (
            (test_rect.x, test_rect.y + test_rect.h),
            (test_rect.w, -test_rect.h),
        )

        test_rect.normalize()

        self.assertEqual(test_rect, expected_normalized_rect)

    @unittest.skipIf(IS_PYPY, "fails on pypy")
    def test_normalize__non_negative(self):
        """Ensures normalize works when width and height are both non-negative.

        Tests combinations of positive and zero values for width and height.
        The normalize method has no impact when both width and height are
        non-negative.
        """
        for size in ((3, 6), (3, 0), (0, 6), (0, 0)):
            test_rect = Rect((1, 2), size)
            expected_normalized_rect = Rect(test_rect)

            test_rect.normalize()

            self.assertEqual(test_rect, expected_normalized_rect)

    def test_x(self):
        """Ensures changing the x attribute moves the rect and does not change
        the rect's size.
        """
        expected_x = 10
        expected_y = 2
        expected_size = (3, 4)
        r = Rect((1, expected_y), expected_size)

        r.x = expected_x

        self.assertEqual(r.x, expected_x)
        self.assertEqual(r.x, r.left)
        self.assertEqual(r.y, expected_y)
        self.assertEqual(r.size, expected_size)

    def test_x__invalid_value(self):
        """Ensures the x attribute handles invalid values correctly."""
        r = Rect(0, 0, 1, 1)

        for value in (None, [], "1", (1,), [1, 2, 3]):
            with self.assertRaises(TypeError):
                r.x = value

    def test_x__del(self):
        """Ensures the x attribute can't be deleted."""
        r = Rect(0, 0, 1, 1)

        with self.assertRaises(AttributeError):
            del r.x

    def test_y(self):
        """Ensures changing the y attribute moves the rect and does not change
        the rect's size.
        """
        expected_x = 1
        expected_y = 20
        expected_size = (3, 4)
        r = Rect((expected_x, 2), expected_size)

        r.y = expected_y

        self.assertEqual(r.y, expected_y)
        self.assertEqual(r.y, r.top)
        self.assertEqual(r.x, expected_x)
        self.assertEqual(r.size, expected_size)

    def test_y__invalid_value(self):
        """Ensures the y attribute handles invalid values correctly."""
        r = Rect(0, 0, 1, 1)

        for value in (None, [], "1", (1,), [1, 2, 3]):
            with self.assertRaises(TypeError):
                r.y = value

    def test_y__del(self):
        """Ensures the y attribute can't be deleted."""
        r = Rect(0, 0, 1, 1)

        with self.assertRaises(AttributeError):
            del r.y

    def test_left(self):
        """Changing the left attribute moves the rect and does not change
        the rect's width
        """
        r = Rect(1, 2, 3, 4)
        new_left = 10

        r.left = new_left
        self.assertEqual(new_left, r.left)
        self.assertEqual(Rect(new_left, 2, 3, 4), r)

    def test_left__invalid_value(self):
        """Ensures the left attribute handles invalid values correctly."""
        r = Rect(0, 0, 1, 1)

        for value in (None, [], "1", (1,), [1, 2, 3]):
            with self.assertRaises(TypeError):
                r.left = value

    def test_left__del(self):
        """Ensures the left attribute can't be deleted."""
        r = Rect(0, 0, 1, 1)

        with self.assertRaises(AttributeError):
            del r.left

    def test_right(self):
        """Changing the right attribute moves the rect and does not change
        the rect's width
        """
        r = Rect(1, 2, 3, 4)
        new_right = r.right + 20
        expected_left = r.left + 20
        old_width = r.width

        r.right = new_right
        self.assertEqual(new_right, r.right)
        self.assertEqual(expected_left, r.left)
        self.assertEqual(old_width, r.width)

    def test_right__invalid_value(self):
        """Ensures the right attribute handles invalid values correctly."""
        r = Rect(0, 0, 1, 1)

        for value in (None, [], "1", (1,), [1, 2, 3]):
            with self.assertRaises(TypeError):
                r.right = value

    def test_right__del(self):
        """Ensures the right attribute can't be deleted."""
        r = Rect(0, 0, 1, 1)

        with self.assertRaises(AttributeError):
            del r.right

    def test_top(self):
        """Changing the top attribute moves the rect and does not change
        the rect's width
        """
        r = Rect(1, 2, 3, 4)
        new_top = 10

        r.top = new_top
        self.assertEqual(Rect(1, new_top, 3, 4), r)
        self.assertEqual(new_top, r.top)

    def test_top__invalid_value(self):
        """Ensures the top attribute handles invalid values correctly."""
        r = Rect(0, 0, 1, 1)

        for value in (None, [], "1", (1,), [1, 2, 3]):
            with self.assertRaises(TypeError):
                r.top = value

    def test_top__del(self):
        """Ensures the top attribute can't be deleted."""
        r = Rect(0, 0, 1, 1)

        with self.assertRaises(AttributeError):
            del r.top

    def test_bottom(self):
        """Changing the bottom attribute moves the rect and does not change
        the rect's height
        """
        r = Rect(1, 2, 3, 4)
        new_bottom = r.bottom + 20
        expected_top = r.top + 20
        old_height = r.height

        r.bottom = new_bottom
        self.assertEqual(new_bottom, r.bottom)
        self.assertEqual(expected_top, r.top)
        self.assertEqual(old_height, r.height)

    def test_bottom__invalid_value(self):
        """Ensures the bottom attribute handles invalid values correctly."""
        r = Rect(0, 0, 1, 1)

        for value in (None, [], "1", (1,), [1, 2, 3]):
            with self.assertRaises(TypeError):
                r.bottom = value

    def test_bottom__del(self):
        """Ensures the bottom attribute can't be deleted."""
        r = Rect(0, 0, 1, 1)

        with self.assertRaises(AttributeError):
            del r.bottom

    def test_centerx(self):
        """Changing the centerx attribute moves the rect and does not change
        the rect's width
        """
        r = Rect(1, 2, 3, 4)
        new_centerx = r.centerx + 20
        expected_left = r.left + 20
        old_width = r.width

        r.centerx = new_centerx
        self.assertEqual(new_centerx, r.centerx)
        self.assertEqual(expected_left, r.left)
        self.assertEqual(old_width, r.width)

    def test_centerx__invalid_value(self):
        """Ensures the centerx attribute handles invalid values correctly."""
        r = Rect(0, 0, 1, 1)

        for value in (None, [], "1", (1,), [1, 2, 3]):
            with self.assertRaises(TypeError):
                r.centerx = value

    def test_centerx__del(self):
        """Ensures the centerx attribute can't be deleted."""
        r = Rect(0, 0, 1, 1)

        with self.assertRaises(AttributeError):
            del r.centerx

    def test_centery(self):
        """Changing the centery attribute moves the rect and does not change
        the rect's width
        """
        r = Rect(1, 2, 3, 4)
        new_centery = r.centery + 20
        expected_top = r.top + 20
        old_height = r.height

        r.centery = new_centery
        self.assertEqual(new_centery, r.centery)
        self.assertEqual(expected_top, r.top)
        self.assertEqual(old_height, r.height)

    def test_centery__invalid_value(self):
        """Ensures the centery attribute handles invalid values correctly."""
        r = Rect(0, 0, 1, 1)

        for value in (None, [], "1", (1,), [1, 2, 3]):
            with self.assertRaises(TypeError):
                r.centery = value

    def test_centery__del(self):
        """Ensures the centery attribute can't be deleted."""
        r = Rect(0, 0, 1, 1)

        with self.assertRaises(AttributeError):
            del r.centery

    def test_topleft(self):
        """Changing the topleft attribute moves the rect and does not change
        the rect's size
        """
        r = Rect(1, 2, 3, 4)
        new_topleft = (r.left + 20, r.top + 30)
        old_size = r.size

        r.topleft = new_topleft
        self.assertEqual(new_topleft, r.topleft)
        self.assertEqual(old_size, r.size)

    def test_topleft__invalid_value(self):
        """Ensures the topleft attribute handles invalid values correctly."""
        r = Rect(0, 0, 1, 1)

        for value in (None, [], "1", 1, (1,), [1, 2, 3]):
            with self.assertRaises(TypeError):
                r.topleft = value

    def test_topleft__del(self):
        """Ensures the topleft attribute can't be deleted."""
        r = Rect(0, 0, 1, 1)

        with self.assertRaises(AttributeError):
            del r.topleft

    def test_bottomleft(self):
        """Changing the bottomleft attribute moves the rect and does not change
        the rect's size
        """
        r = Rect(1, 2, 3, 4)
        new_bottomleft = (r.left + 20, r.bottom + 30)
        expected_topleft = (r.left + 20, r.top + 30)
        old_size = r.size

        r.bottomleft = new_bottomleft
        self.assertEqual(new_bottomleft, r.bottomleft)
        self.assertEqual(expected_topleft, r.topleft)
        self.assertEqual(old_size, r.size)

    def test_bottomleft__invalid_value(self):
        """Ensures the bottomleft attribute handles invalid values correctly."""
        r = Rect(0, 0, 1, 1)

        for value in (None, [], "1", 1, (1,), [1, 2, 3]):
            with self.assertRaises(TypeError):
                r.bottomleft = value

    def test_bottomleft__del(self):
        """Ensures the bottomleft attribute can't be deleted."""
        r = Rect(0, 0, 1, 1)

        with self.assertRaises(AttributeError):
            del r.bottomleft

    def test_topright(self):
        """Changing the topright attribute moves the rect and does not change
        the rect's size
        """
        r = Rect(1, 2, 3, 4)
        new_topright = (r.right + 20, r.top + 30)
        expected_topleft = (r.left + 20, r.top + 30)
        old_size = r.size

        r.topright = new_topright
        self.assertEqual(new_topright, r.topright)
        self.assertEqual(expected_topleft, r.topleft)
        self.assertEqual(old_size, r.size)

    def test_topright__invalid_value(self):
        """Ensures the topright attribute handles invalid values correctly."""
        r = Rect(0, 0, 1, 1)

        for value in (None, [], "1", 1, (1,), [1, 2, 3]):
            with self.assertRaises(TypeError):
                r.topright = value

    def test_topright__del(self):
        """Ensures the topright attribute can't be deleted."""
        r = Rect(0, 0, 1, 1)

        with self.assertRaises(AttributeError):
            del r.topright

    def test_bottomright(self):
        """Changing the bottomright attribute moves the rect and does not change
        the rect's size
        """
        r = Rect(1, 2, 3, 4)
        new_bottomright = (r.right + 20, r.bottom + 30)
        expected_topleft = (r.left + 20, r.top + 30)
        old_size = r.size

        r.bottomright = new_bottomright
        self.assertEqual(new_bottomright, r.bottomright)
        self.assertEqual(expected_topleft, r.topleft)
        self.assertEqual(old_size, r.size)

    def test_bottomright__invalid_value(self):
        """Ensures the bottomright attribute handles invalid values correctly."""
        r = Rect(0, 0, 1, 1)

        for value in (None, [], "1", 1, (1,), [1, 2, 3]):
            with self.assertRaises(TypeError):
                r.bottomright = value

    def test_bottomright__del(self):
        """Ensures the bottomright attribute can't be deleted."""
        r = Rect(0, 0, 1, 1)

        with self.assertRaises(AttributeError):
            del r.bottomright

    def test_center(self):
        """Changing the center attribute moves the rect and does not change
        the rect's size
        """
        r = Rect(1, 2, 3, 4)
        new_center = (r.centerx + 20, r.centery + 30)
        expected_topleft = (r.left + 20, r.top + 30)
        old_size = r.size

        r.center = new_center
        self.assertEqual(new_center, r.center)
        self.assertEqual(expected_topleft, r.topleft)
        self.assertEqual(old_size, r.size)

    def test_center__invalid_value(self):
        """Ensures the center attribute handles invalid values correctly."""
        r = Rect(0, 0, 1, 1)

        for value in (None, [], "1", 1, (1,), [1, 2, 3]):
            with self.assertRaises(TypeError):
                r.center = value

    def test_center__del(self):
        """Ensures the center attribute can't be deleted."""
        r = Rect(0, 0, 1, 1)

        with self.assertRaises(AttributeError):
            del r.center

    def test_midleft(self):
        """Changing the midleft attribute moves the rect and does not change
        the rect's size
        """
        r = Rect(1, 2, 3, 4)
        new_midleft = (r.left + 20, r.centery + 30)
        expected_topleft = (r.left + 20, r.top + 30)
        old_size = r.size

        r.midleft = new_midleft
        self.assertEqual(new_midleft, r.midleft)
        self.assertEqual(expected_topleft, r.topleft)
        self.assertEqual(old_size, r.size)

    def test_midleft__invalid_value(self):
        """Ensures the midleft attribute handles invalid values correctly."""
        r = Rect(0, 0, 1, 1)

        for value in (None, [], "1", 1, (1,), [1, 2, 3]):
            with self.assertRaises(TypeError):
                r.midleft = value

    def test_midleft__del(self):
        """Ensures the midleft attribute can't be deleted."""
        r = Rect(0, 0, 1, 1)

        with self.assertRaises(AttributeError):
            del r.midleft

    def test_midright(self):
        """Changing the midright attribute moves the rect and does not change
        the rect's size
        """
        r = Rect(1, 2, 3, 4)
        new_midright = (r.right + 20, r.centery + 30)
        expected_topleft = (r.left + 20, r.top + 30)
        old_size = r.size

        r.midright = new_midright
        self.assertEqual(new_midright, r.midright)
        self.assertEqual(expected_topleft, r.topleft)
        self.assertEqual(old_size, r.size)

    def test_midright__invalid_value(self):
        """Ensures the midright attribute handles invalid values correctly."""
        r = Rect(0, 0, 1, 1)

        for value in (None, [], "1", 1, (1,), [1, 2, 3]):
            with self.assertRaises(TypeError):
                r.midright = value

    def test_midright__del(self):
        """Ensures the midright attribute can't be deleted."""
        r = Rect(0, 0, 1, 1)

        with self.assertRaises(AttributeError):
            del r.midright

    def test_midtop(self):
        """Changing the midtop attribute moves the rect and does not change
        the rect's size
        """
        r = Rect(1, 2, 3, 4)
        new_midtop = (r.centerx + 20, r.top + 30)
        expected_topleft = (r.left + 20, r.top + 30)
        old_size = r.size

        r.midtop = new_midtop
        self.assertEqual(new_midtop, r.midtop)
        self.assertEqual(expected_topleft, r.topleft)
        self.assertEqual(old_size, r.size)

    def test_midtop__invalid_value(self):
        """Ensures the midtop attribute handles invalid values correctly."""
        r = Rect(0, 0, 1, 1)

        for value in (None, [], "1", 1, (1,), [1, 2, 3]):
            with self.assertRaises(TypeError):
                r.midtop = value

    def test_midtop__del(self):
        """Ensures the midtop attribute can't be deleted."""
        r = Rect(0, 0, 1, 1)

        with self.assertRaises(AttributeError):
            del r.midtop

    def test_midbottom(self):
        """Changing the midbottom attribute moves the rect and does not change
        the rect's size
        """
        r = Rect(1, 2, 3, 4)
        new_midbottom = (r.centerx + 20, r.bottom + 30)
        expected_topleft = (r.left + 20, r.top + 30)
        old_size = r.size

        r.midbottom = new_midbottom
        self.assertEqual(new_midbottom, r.midbottom)
        self.assertEqual(expected_topleft, r.topleft)
        self.assertEqual(old_size, r.size)

    def test_midbottom__invalid_value(self):
        """Ensures the midbottom attribute handles invalid values correctly."""
        r = Rect(0, 0, 1, 1)

        for value in (None, [], "1", 1, (1,), [1, 2, 3]):
            with self.assertRaises(TypeError):
                r.midbottom = value

    def test_midbottom__del(self):
        """Ensures the midbottom attribute can't be deleted."""
        r = Rect(0, 0, 1, 1)

        with self.assertRaises(AttributeError):
            del r.midbottom

    def test_width(self):
        """Changing the width resizes the rect from the top-left corner"""
        r = Rect(1, 2, 3, 4)
        new_width = 10
        old_topleft = r.topleft
        old_height = r.height

        r.width = new_width
        self.assertEqual(new_width, r.width)
        self.assertEqual(old_height, r.height)
        self.assertEqual(old_topleft, r.topleft)

    def test_width__invalid_value(self):
        """Ensures the width attribute handles invalid values correctly."""
        r = Rect(0, 0, 1, 1)

        for value in (None, [], "1", (1,), [1, 2, 3]):
            with self.assertRaises(TypeError):
                r.width = value

    def test_width__del(self):
        """Ensures the width attribute can't be deleted."""
        r = Rect(0, 0, 1, 1)

        with self.assertRaises(AttributeError):
            del r.width

    def test_height(self):
        """Changing the height resizes the rect from the top-left corner"""
        r = Rect(1, 2, 3, 4)
        new_height = 10
        old_topleft = r.topleft
        old_width = r.width

        r.height = new_height
        self.assertEqual(new_height, r.height)
        self.assertEqual(old_width, r.width)
        self.assertEqual(old_topleft, r.topleft)

    def test_height__invalid_value(self):
        """Ensures the height attribute handles invalid values correctly."""
        r = Rect(0, 0, 1, 1)

        for value in (None, [], "1", (1,), [1, 2, 3]):
            with self.assertRaises(TypeError):
                r.height = value

    def test_height__del(self):
        """Ensures the height attribute can't be deleted."""
        r = Rect(0, 0, 1, 1)

        with self.assertRaises(AttributeError):
            del r.height

    def test_size(self):
        """Changing the size resizes the rect from the top-left corner"""
        r = Rect(1, 2, 3, 4)
        new_size = (10, 20)
        old_topleft = r.topleft

        r.size = new_size
        self.assertEqual(new_size, r.size)
        self.assertEqual(old_topleft, r.topleft)

    def test_size__invalid_value(self):
        """Ensures the size attribute handles invalid values correctly."""
        r = Rect(0, 0, 1, 1)

        for value in (None, [], "1", 1, (1,), [1, 2, 3]):
            with self.assertRaises(TypeError):
                r.size = value

    def test_size__del(self):
        """Ensures the size attribute can't be deleted."""
        r = Rect(0, 0, 1, 1)

        with self.assertRaises(AttributeError):
            del r.size

    def test_contains(self):
        r = Rect(1, 2, 3, 4)

        self.assertTrue(
            r.contains(Rect(2, 3, 1, 1)), "r does not contain Rect(2, 3, 1, 1)"
        )
        self.assertTrue(Rect(2, 3, 1, 1) in r, "r does not contain Rect(2, 3, 1, 1) 2")
        self.assertTrue(
            r.contains(Rect(r)), "r does not contain the same rect as itself"
        )
        self.assertTrue(r in Rect(r), "r does not contain the same rect as itself")
        self.assertTrue(
            r.contains(Rect(2, 3, 0, 0)),
            "r does not contain an empty rect within its bounds",
        )
        self.assertTrue(
            Rect(2, 3, 0, 0) in r,
            "r does not contain an empty rect within its bounds",
        )
        self.assertFalse(r.contains(Rect(0, 0, 1, 2)), "r contains Rect(0, 0, 1, 2)")
        self.assertFalse(r.contains(Rect(4, 6, 1, 1)), "r contains Rect(4, 6, 1, 1)")
        self.assertFalse(r.contains(Rect(4, 6, 0, 0)), "r contains Rect(4, 6, 0, 0)")
        self.assertFalse(Rect(0, 0, 1, 2) in r, "r contains Rect(0, 0, 1, 2)")
        self.assertFalse(Rect(4, 6, 1, 1) in r, "r contains Rect(4, 6, 1, 1)")
        self.assertFalse(Rect(4, 6, 0, 0) in r, "r contains Rect(4, 6, 0, 0)")
        self.assertTrue(2 in Rect(0, 0, 1, 2), "r does not contain 2")
        self.assertFalse(3 in Rect(0, 0, 1, 2), "r contains 3")

    def test_collidepoint(self):
        r = Rect(1, 2, 3, 4)

        self.assertTrue(
            r.collidepoint(r.left, r.top), "r does not collide with point (left, top)"
        )
        self.assertFalse(
            r.collidepoint(r.left - 1, r.top), "r collides with point (left - 1, top)"
        )
        self.assertFalse(
            r.collidepoint(r.left, r.top - 1), "r collides with point (left, top - 1)"
        )
        self.assertFalse(
            r.collidepoint(r.left - 1, r.top - 1),
            "r collides with point (left - 1, top - 1)",
        )

        self.assertTrue(
            r.collidepoint(r.right - 1, r.bottom - 1),
            "r does not collide with point (right - 1, bottom - 1)",
        )
        self.assertFalse(
            r.collidepoint(r.right, r.bottom), "r collides with point (right, bottom)"
        )
        self.assertFalse(
            r.collidepoint(r.right - 1, r.bottom),
            "r collides with point (right - 1, bottom)",
        )
        self.assertFalse(
            r.collidepoint(r.right, r.bottom - 1),
            "r collides with point (right, bottom - 1)",
        )

    def test_inflate__larger(self):
        """The inflate method inflates around the center of the rectangle"""
        r = Rect(2, 4, 6, 8)
        r2 = r.inflate(4, 6)

        self.assertEqual(r.center, r2.center)
        self.assertEqual(r.left - 2, r2.left)
        self.assertEqual(r.top - 3, r2.top)
        self.assertEqual(r.right + 2, r2.right)
        self.assertEqual(r.bottom + 3, r2.bottom)
        self.assertEqual(r.width + 4, r2.width)
        self.assertEqual(r.height + 6, r2.height)

    def test_inflate__smaller(self):
        """The inflate method inflates around the center of the rectangle"""
        r = Rect(2, 4, 6, 8)
        r2 = r.inflate(-4, -6)

        self.assertEqual(r.center, r2.center)
        self.assertEqual(r.left + 2, r2.left)
        self.assertEqual(r.top + 3, r2.top)
        self.assertEqual(r.right - 2, r2.right)
        self.assertEqual(r.bottom - 3, r2.bottom)
        self.assertEqual(r.width - 4, r2.width)
        self.assertEqual(r.height - 6, r2.height)

    def test_inflate_ip__larger(self):
        """The inflate_ip method inflates around the center of the rectangle"""
        r = Rect(2, 4, 6, 8)
        r2 = Rect(r)
        r2.inflate_ip(-4, -6)

        self.assertEqual(r.center, r2.center)
        self.assertEqual(r.left + 2, r2.left)
        self.assertEqual(r.top + 3, r2.top)
        self.assertEqual(r.right - 2, r2.right)
        self.assertEqual(r.bottom - 3, r2.bottom)
        self.assertEqual(r.width - 4, r2.width)
        self.assertEqual(r.height - 6, r2.height)

    def test_inflate_ip__smaller(self):
        """The inflate method inflates around the center of the rectangle"""
        r = Rect(2, 4, 6, 8)
        r2 = Rect(r)
        r2.inflate_ip(-4, -6)

        self.assertEqual(r.center, r2.center)
        self.assertEqual(r.left + 2, r2.left)
        self.assertEqual(r.top + 3, r2.top)
        self.assertEqual(r.right - 2, r2.right)
        self.assertEqual(r.bottom - 3, r2.bottom)
        self.assertEqual(r.width - 4, r2.width)
        self.assertEqual(r.height - 6, r2.height)

    def test_clamp(self):
        r = Rect(10, 10, 10, 10)
        c = Rect(19, 12, 5, 5).clamp(r)
        self.assertEqual(c.right, r.right)
        self.assertEqual(c.top, 12)
        c = Rect(1, 2, 3, 4).clamp(r)
        self.assertEqual(c.topleft, r.topleft)
        c = Rect(5, 500, 22, 33).clamp(r)
        self.assertEqual(c.center, r.center)

    def test_clamp_ip(self):
        r = Rect(10, 10, 10, 10)
        c = Rect(19, 12, 5, 5)
        c.clamp_ip(r)
        self.assertEqual(c.right, r.right)
        self.assertEqual(c.top, 12)
        c = Rect(1, 2, 3, 4)
        c.clamp_ip(r)
        self.assertEqual(c.topleft, r.topleft)
        c = Rect(5, 500, 22, 33)
        c.clamp_ip(r)
        self.assertEqual(c.center, r.center)

    def test_clip(self):
        r1 = Rect(1, 2, 3, 4)
        self.assertEqual(Rect(1, 2, 2, 2), r1.clip(Rect(0, 0, 3, 4)))
        self.assertEqual(Rect(2, 2, 2, 4), r1.clip(Rect(2, 2, 10, 20)))
        self.assertEqual(Rect(2, 3, 1, 2), r1.clip(Rect(2, 3, 1, 2)))
        self.assertEqual((0, 0), r1.clip(20, 30, 5, 6).size)
        self.assertEqual(
            r1, r1.clip(Rect(r1)), "r1 does not clip an identical rect to itself"
        )

    def test_clipline(self):
        """Ensures clipline handles four int parameters.

        Tests the clipline(x1, y1, x2, y2) format.
        """
        rect = Rect((1, 2), (35, 40))
        x1 = 5
        y1 = 6
        x2 = 11
        y2 = 19
        expected_line = ((x1, y1), (x2, y2))

        clipped_line = rect.clipline(x1, y1, x2, y2)

        self.assertIsInstance(clipped_line, tuple)
        self.assertTupleEqual(clipped_line, expected_line)

    def test_clipline__two_sequences(self):
        """Ensures clipline handles a sequence of two sequences.

        Tests the clipline((x1, y1), (x2, y2)) format.
        Tests the sequences as different types.
        """
        rect = Rect((1, 2), (35, 40))
        pt1 = (5, 6)
        pt2 = (11, 19)

        INNER_SEQUENCES = (list, tuple, Vector2)
        expected_line = (pt1, pt2)

        for inner_seq1 in INNER_SEQUENCES:
            endpt1 = inner_seq1(pt1)

            for inner_seq2 in INNER_SEQUENCES:
                clipped_line = rect.clipline((endpt1, inner_seq2(pt2)))

                self.assertIsInstance(clipped_line, tuple)
                self.assertTupleEqual(clipped_line, expected_line)

    def test_clipline__sequence_of_four_ints(self):
        """Ensures clipline handles a sequence of four ints.

        Tests the clipline((x1, y1, x2, y2)) format.
        Tests the sequence as different types.
        """
        rect = Rect((1, 2), (35, 40))
        line = (5, 6, 11, 19)
        expected_line = ((line[0], line[1]), (line[2], line[3]))

        for outer_seq in (list, tuple):
            clipped_line = rect.clipline(outer_seq(line))

            self.assertIsInstance(clipped_line, tuple)
            self.assertTupleEqual(clipped_line, expected_line)

    def test_clipline__sequence_of_two_sequences(self):
        """Ensures clipline handles a sequence of two sequences.

        Tests the clipline(((x1, y1), (x2, y2))) format.
        Tests the sequences as different types.
        """
        rect = Rect((1, 2), (35, 40))
        pt1 = (5, 6)
        pt2 = (11, 19)

        INNER_SEQUENCES = (list, tuple, Vector2)
        expected_line = (pt1, pt2)

        for inner_seq1 in INNER_SEQUENCES:
            endpt1 = inner_seq1(pt1)

            for inner_seq2 in INNER_SEQUENCES:
                endpt2 = inner_seq2(pt2)

                for outer_seq in (list, tuple):
                    clipped_line = rect.clipline(outer_seq((endpt1, endpt2)))

                    self.assertIsInstance(clipped_line, tuple)
                    self.assertTupleEqual(clipped_line, expected_line)

    def test_clipline__floats(self):
        """Ensures clipline handles float parameters."""
        rect = Rect((1, 2), (35, 40))
        x1 = 5.9
        y1 = 6.9
        x2 = 11.9
        y2 = 19.9

        # Floats are truncated.
        expected_line = (
            (math.floor(x1), math.floor(y1)),
            (math.floor(x2), math.floor(y2)),
        )

        clipped_line = rect.clipline(x1, y1, x2, y2)

        self.assertIsInstance(clipped_line, tuple)
        self.assertTupleEqual(clipped_line, expected_line)

    def test_clipline__no_overlap(self):
        """Ensures lines that do not overlap the rect are not clipped."""
        rect = Rect((10, 25), (15, 20))
        # Use a bigger rect to help create test lines.
        big_rect = rect.inflate(2, 2)
        lines = (
            (big_rect.bottomleft, big_rect.topleft),  # Left edge.
            (big_rect.topleft, big_rect.topright),  # Top edge.
            (big_rect.topright, big_rect.bottomright),  # Right edge.
            (big_rect.bottomright, big_rect.bottomleft),
        )  # Bottom edge.
        expected_line = ()

        # Test lines outside rect.
        for line in lines:
            clipped_line = rect.clipline(line)

            self.assertTupleEqual(clipped_line, expected_line)

    def test_clipline__both_endpoints_outside(self):
        """Ensures lines that overlap the rect are clipped.

        Testing lines with both endpoints outside the rect.
        """
        rect = Rect((0, 0), (20, 20))
        # Use a bigger rect to help create test lines.
        big_rect = rect.inflate(2, 2)

        # Create a dict of lines and expected results.
        line_dict = {
            (big_rect.midleft, big_rect.midright): (
                rect.midleft,
                (rect.midright[0] - 1, rect.midright[1]),
            ),
            (big_rect.midtop, big_rect.midbottom): (
                rect.midtop,
                (rect.midbottom[0], rect.midbottom[1] - 1),
            ),
            # Diagonals.
            (big_rect.topleft, big_rect.bottomright): (
                rect.topleft,
                (rect.bottomright[0] - 1, rect.bottomright[1] - 1),
            ),
            # This line needs a small adjustment to make sure it intersects
            # the rect correctly.
            (
                (big_rect.topright[0] - 1, big_rect.topright[1]),
                (big_rect.bottomleft[0], big_rect.bottomleft[1] - 1),
            ): (
                (rect.topright[0] - 1, rect.topright[1]),
                (rect.bottomleft[0], rect.bottomleft[1] - 1),
            ),
        }

        for line, expected_line in line_dict.items():
            clipped_line = rect.clipline(line)

            self.assertTupleEqual(clipped_line, expected_line)

            # Swap endpoints to test for symmetry.
            expected_line = (expected_line[1], expected_line[0])

            clipped_line = rect.clipline((line[1], line[0]))

            self.assertTupleEqual(clipped_line, expected_line)

    def test_clipline__both_endpoints_inside(self):
        """Ensures lines that overlap the rect are clipped.

        Testing lines with both endpoints inside the rect.
        """
        rect = Rect((-10, -5), (20, 20))
        # Use a smaller rect to help create test lines.
        small_rect = rect.inflate(-2, -2)

        lines = (
            (small_rect.midleft, small_rect.midright),
            (small_rect.midtop, small_rect.midbottom),
            # Diagonals.
            (small_rect.topleft, small_rect.bottomright),
            (small_rect.topright, small_rect.bottomleft),
        )

        for line in lines:
            expected_line = line

            clipped_line = rect.clipline(line)

            self.assertTupleEqual(clipped_line, expected_line)

            # Swap endpoints to test for symmetry.
            expected_line = (expected_line[1], expected_line[0])

            clipped_line = rect.clipline((line[1], line[0]))

            self.assertTupleEqual(clipped_line, expected_line)

    def test_clipline__endpoints_inside_and_outside(self):
        """Ensures lines that overlap the rect are clipped.

        Testing lines with one endpoint outside the rect and the other is
        inside the rect.
        """
        rect = Rect((0, 0), (21, 21))
        # Use a bigger rect to help create test lines.
        big_rect = rect.inflate(2, 2)

        # Create a dict of lines and expected results.
        line_dict = {
            (big_rect.midleft, rect.center): (rect.midleft, rect.center),
            (big_rect.midtop, rect.center): (rect.midtop, rect.center),
            (big_rect.midright, rect.center): (
                (rect.midright[0] - 1, rect.midright[1]),
                rect.center,
            ),
            (big_rect.midbottom, rect.center): (
                (rect.midbottom[0], rect.midbottom[1] - 1),
                rect.center,
            ),
            # Diagonals.
            (big_rect.topleft, rect.center): (rect.topleft, rect.center),
            (big_rect.topright, rect.center): (
                (rect.topright[0] - 1, rect.topright[1]),
                rect.center,
            ),
            (big_rect.bottomright, rect.center): (
                (rect.bottomright[0] - 1, rect.bottomright[1] - 1),
                rect.center,
            ),
            # This line needs a small adjustment to make sure it intersects
            # the rect correctly.
            ((big_rect.bottomleft[0], big_rect.bottomleft[1] - 1), rect.center): (
                (rect.bottomleft[0], rect.bottomleft[1] - 1),
                rect.center,
            ),
        }

        for line, expected_line in line_dict.items():
            clipped_line = rect.clipline(line)

            self.assertTupleEqual(clipped_line, expected_line)

            # Swap endpoints to test for symmetry.
            expected_line = (expected_line[1], expected_line[0])

            clipped_line = rect.clipline((line[1], line[0]))

            self.assertTupleEqual(clipped_line, expected_line)

    def test_clipline__edges(self):
        """Ensures clipline properly clips line that are along the rect edges."""
        rect = Rect((10, 25), (15, 20))

        # Create a dict of edges and expected results.
        edge_dict = {
            # Left edge.
            (rect.bottomleft, rect.topleft): (
                (rect.bottomleft[0], rect.bottomleft[1] - 1),
                rect.topleft,
            ),
            # Top edge.
            (rect.topleft, rect.topright): (
                rect.topleft,
                (rect.topright[0] - 1, rect.topright[1]),
            ),
            # Right edge.
            (rect.topright, rect.bottomright): (),
            # Bottom edge.
            (rect.bottomright, rect.bottomleft): (),
        }

        for edge, expected_line in edge_dict.items():
            clipped_line = rect.clipline(edge)

            self.assertTupleEqual(clipped_line, expected_line)

            # Swap endpoints to test for symmetry.
            if expected_line:
                expected_line = (expected_line[1], expected_line[0])

            clipped_line = rect.clipline((edge[1], edge[0]))

            self.assertTupleEqual(clipped_line, expected_line)

    def test_clipline__equal_endpoints_with_overlap(self):
        """Ensures clipline handles lines with both endpoints the same.

        Testing lines that overlap the rect.
        """
        rect = Rect((10, 25), (15, 20))

        # Test all the points in and on a rect.
        pts = (
            (x, y)
            for x in range(rect.left, rect.right)
            for y in range(rect.top, rect.bottom)
        )

        for pt in pts:
            expected_line = (pt, pt)

            clipped_line = rect.clipline((pt, pt))

            self.assertTupleEqual(clipped_line, expected_line)

    def test_clipline__equal_endpoints_no_overlap(self):
        """Ensures clipline handles lines with both endpoints the same.

        Testing lines that do not overlap the rect.
        """
        expected_line = ()
        rect = Rect((10, 25), (15, 20))

        # Test points outside rect.
        for pt in test_utils.rect_perimeter_pts(rect.inflate(2, 2)):
            clipped_line = rect.clipline((pt, pt))

            self.assertTupleEqual(clipped_line, expected_line)

    def test_clipline__zero_size_rect(self):
        """Ensures clipline handles zero sized rects correctly."""
        expected_line = ()

        for size in ((0, 15), (15, 0), (0, 0)):
            rect = Rect((10, 25), size)

            clipped_line = rect.clipline(rect.topleft, rect.topleft)

            self.assertTupleEqual(clipped_line, expected_line)

    def test_clipline__negative_size_rect(self):
        """Ensures clipline handles negative sized rects correctly."""
        expected_line = ()

        for size in ((-15, 20), (15, -20), (-15, -20)):
            rect = Rect((10, 25), size)
            norm_rect = rect.copy()
            norm_rect.normalize()
            # Use a bigger rect to help create test lines.
            big_rect = norm_rect.inflate(2, 2)

            # Create a dict of lines and expected results. Some line have both
            # endpoints outside the rect and some have one inside and one
            # outside.
            line_dict = {
                (big_rect.midleft, big_rect.midright): (
                    norm_rect.midleft,
                    (norm_rect.midright[0] - 1, norm_rect.midright[1]),
                ),
                (big_rect.midtop, big_rect.midbottom): (
                    norm_rect.midtop,
                    (norm_rect.midbottom[0], norm_rect.midbottom[1] - 1),
                ),
                (big_rect.midleft, norm_rect.center): (
                    norm_rect.midleft,
                    norm_rect.center,
                ),
                (big_rect.midtop, norm_rect.center): (
                    norm_rect.midtop,
                    norm_rect.center,
                ),
                (big_rect.midright, norm_rect.center): (
                    (norm_rect.midright[0] - 1, norm_rect.midright[1]),
                    norm_rect.center,
                ),
                (big_rect.midbottom, norm_rect.center): (
                    (norm_rect.midbottom[0], norm_rect.midbottom[1] - 1),
                    norm_rect.center,
                ),
            }

            for line, expected_line in line_dict.items():
                clipped_line = rect.clipline(line)

                # Make sure rect wasn't normalized.
                self.assertNotEqual(rect, norm_rect)
                self.assertTupleEqual(clipped_line, expected_line)

                # Swap endpoints to test for symmetry.
                expected_line = (expected_line[1], expected_line[0])

                clipped_line = rect.clipline((line[1], line[0]))

                self.assertTupleEqual(clipped_line, expected_line)

    def test_clipline__invalid_line(self):
        """Ensures clipline handles invalid lines correctly."""
        rect = Rect((0, 0), (10, 20))
        invalid_lines = (
            (),
            (1,),
            (1, 2),
            (1, 2, 3),
            (1, 2, 3, 4, 5),
            ((1, 2),),
            ((1, 2), (3,)),
            ((1, 2), 3),
            ((1, 2, 5), (3, 4)),
            ((1, 2), (3, 4, 5)),
            ((1, 2), (3, 4), (5, 6)),
        )

        for line in invalid_lines:
            with self.assertRaises(TypeError):
                clipped_line = rect.clipline(line)

            with self.assertRaises(TypeError):
                clipped_line = rect.clipline(*line)

    @unittest.skipIf(IS_PYPY, "fails on pypy sometimes")
    def test_move(self):
        r = Rect(1, 2, 3, 4)
        move_x = 10
        move_y = 20
        r2 = r.move(move_x, move_y)
        expected_r2 = Rect(r.left + move_x, r.top + move_y, r.width, r.height)
        self.assertEqual(expected_r2, r2)

    @unittest.skipIf(IS_PYPY, "fails on pypy sometimes")
    def test_move_ip(self):
        r = Rect(1, 2, 3, 4)
        r2 = Rect(r)
        move_x = 10
        move_y = 20
        r2.move_ip(move_x, move_y)
        expected_r2 = Rect(r.left + move_x, r.top + move_y, r.width, r.height)
        self.assertEqual(expected_r2, r2)

    def test_update_XYWidthHeight(self):
        """Test update with 4 int values(x, y, w, h)"""
        rect = Rect(0, 0, 1, 1)
        rect.update(1, 2, 3, 4)

        self.assertEqual(1, rect.left)
        self.assertEqual(2, rect.top)
        self.assertEqual(3, rect.width)
        self.assertEqual(4, rect.height)

    def test_update__TopLeftSize(self):
        """Test update with 2 tuples((x, y), (w, h))"""
        rect = Rect(0, 0, 1, 1)
        rect.update((1, 2), (3, 4))

        self.assertEqual(1, rect.left)
        self.assertEqual(2, rect.top)
        self.assertEqual(3, rect.width)
        self.assertEqual(4, rect.height)

    def test_update__List(self):
        """Test update with list"""
        rect = Rect(0, 0, 1, 1)
        rect2 = [1, 2, 3, 4]
        rect.update(rect2)

        self.assertEqual(1, rect.left)
        self.assertEqual(2, rect.top)
        self.assertEqual(3, rect.width)
        self.assertEqual(4, rect.height)

    def test_update__RectObject(self):
        """Test update with other rect object"""
        rect = Rect(0, 0, 1, 1)
        rect2 = Rect(1, 2, 3, 4)
        rect.update(rect2)

        self.assertEqual(1, rect.left)
        self.assertEqual(2, rect.top)
        self.assertEqual(3, rect.width)
        self.assertEqual(4, rect.height)

    def test_union(self):
        r1 = Rect(1, 1, 1, 2)
        r2 = Rect(-2, -2, 1, 2)
        self.assertEqual(Rect(-2, -2, 4, 5), r1.union(r2))

    def test_union__with_identical_Rect(self):
        r1 = Rect(1, 2, 3, 4)
        self.assertEqual(r1, r1.union(Rect(r1)))

    def test_union_ip(self):
        r1 = Rect(1, 1, 1, 2)
        r2 = Rect(-2, -2, 1, 2)
        r1.union_ip(r2)
        self.assertEqual(Rect(-2, -2, 4, 5), r1)

    def test_unionall(self):
        r1 = Rect(0, 0, 1, 1)
        r2 = Rect(-2, -2, 1, 1)
        r3 = Rect(2, 2, 1, 1)

        r4 = r1.unionall([r2, r3])
        self.assertEqual(Rect(-2, -2, 5, 5), r4)

    def test_unionall__invalid_rect_format(self):
        """Ensures unionall correctly handles invalid rect parameters."""
        numbers = [0, 1.2, 2, 3.3]
        strs = ["a", "b", "c"]
        nones = [None, None]

        for invalid_rects in (numbers, strs, nones):
            with self.assertRaises(TypeError):
                Rect(0, 0, 1, 1).unionall(invalid_rects)

    def test_unionall_ip(self):
        r1 = Rect(0, 0, 1, 1)
        r2 = Rect(-2, -2, 1, 1)
        r3 = Rect(2, 2, 1, 1)

        r1.unionall_ip([r2, r3])
        self.assertEqual(Rect(-2, -2, 5, 5), r1)

        # Bug for an empty list. Would return a Rect instead of None.
        self.assertTrue(r1.unionall_ip([]) is None)

    def test_unionall__invalid_rect_format(self):
        """Ensures unionall_ip correctly handles invalid rect parameters."""
        numbers = [0, 1.2, 2, 3.3]
        strs = ["a", "b", "c"]
        nones = [None, None]

        for invalid_rects in (numbers, strs, nones):
            with self.assertRaises(TypeError):
                Rect(0, 0, 1, 1).unionall_ip(invalid_rects)

    def test_colliderect(self):
        r1 = Rect(1, 2, 3, 4)
        self.assertTrue(
            r1.colliderect(Rect(0, 0, 2, 3)),
            "r1 does not collide with Rect(0, 0, 2, 3)",
        )
        self.assertFalse(
            r1.colliderect(Rect(0, 0, 1, 2)), "r1 collides with Rect(0, 0, 1, 2)"
        )
        self.assertFalse(
            r1.colliderect(Rect(r1.right, r1.bottom, 2, 2)),
            "r1 collides with Rect(r1.right, r1.bottom, 2, 2)",
        )
        self.assertTrue(
            r1.colliderect(Rect(r1.left + 1, r1.top + 1, r1.width - 2, r1.height - 2)),
            "r1 does not collide with Rect(r1.left + 1, r1.top + 1, "
            + "r1.width - 2, r1.height - 2)",
        )
        self.assertTrue(
            r1.colliderect(Rect(r1.left - 1, r1.top - 1, r1.width + 2, r1.height + 2)),
            "r1 does not collide with Rect(r1.left - 1, r1.top - 1, "
            + "r1.width + 2, r1.height + 2)",
        )
        self.assertTrue(
            r1.colliderect(Rect(r1)), "r1 does not collide with an identical rect"
        )
        self.assertFalse(
            r1.colliderect(Rect(r1.right, r1.bottom, 0, 0)),
            "r1 collides with Rect(r1.right, r1.bottom, 0, 0)",
        )
        self.assertFalse(
            r1.colliderect(Rect(r1.right, r1.bottom, 1, 1)),
            "r1 collides with Rect(r1.right, r1.bottom, 1, 1)",
        )

    @unittest.skipIf(IS_PYPY, "fails on pypy3 sometimes")
    def testEquals(self):
        """check to see how the rect uses __eq__"""
        r1 = Rect(1, 2, 3, 4)
        r2 = Rect(10, 20, 30, 40)
        r3 = (10, 20, 30, 40)
        r4 = Rect(10, 20, 30, 40)

        class foo(Rect):
            def __eq__(self, other):
                return id(self) == id(other)

            def __ne__(self, other):
                return id(self) != id(other)

        class foo2(Rect):
            pass

        r5 = foo(10, 20, 30, 40)
        r6 = foo2(10, 20, 30, 40)

        self.assertNotEqual(r5, r2)

        # because we define equality differently for this subclass.
        self.assertEqual(r6, r2)

        rect_list = [r1, r2, r3, r4, r6]

        # see if we can remove 4 of these.
        rect_list.remove(r2)
        rect_list.remove(r2)
        rect_list.remove(r2)
        rect_list.remove(r2)
        self.assertRaises(ValueError, rect_list.remove, r2)

    def test_collidedict(self):
        """Ensures collidedict detects collisions."""
        rect = Rect(1, 1, 10, 10)

        collide_item1 = ("collide 1", rect.copy())
        collide_item2 = ("collide 2", Rect(5, 5, 10, 10))
        no_collide_item1 = ("no collide 1", Rect(60, 60, 10, 10))
        no_collide_item2 = ("no collide 2", Rect(70, 70, 10, 10))

        # Dict to check collisions with values.
        rect_values = dict(
            (collide_item1, collide_item2, no_collide_item1, no_collide_item2)
        )
        value_collide_items = (collide_item1, collide_item2)

        # Dict to check collisions with keys.
        rect_keys = {tuple(v): k for k, v in rect_values.items()}
        key_collide_items = tuple((tuple(v), k) for k, v in value_collide_items)

        for use_values in (True, False):
            if use_values:
                expected_items = value_collide_items
                d = rect_values
            else:
                expected_items = key_collide_items
                d = rect_keys

            collide_item = rect.collidedict(d, use_values)

            # The detected collision could be any of the possible items.
            self.assertIn(collide_item, expected_items)

    def test_collidedict__no_collision(self):
        """Ensures collidedict returns None when no collisions."""
        rect = Rect(1, 1, 10, 10)

        no_collide_item1 = ("no collide 1", Rect(50, 50, 10, 10))
        no_collide_item2 = ("no collide 2", Rect(60, 60, 10, 10))
        no_collide_item3 = ("no collide 3", Rect(70, 70, 10, 10))

        # Dict to check collisions with values.
        rect_values = dict((no_collide_item1, no_collide_item2, no_collide_item3))

        # Dict to check collisions with keys.
        rect_keys = {tuple(v): k for k, v in rect_values.items()}

        for use_values in (True, False):
            d = rect_values if use_values else rect_keys

            collide_item = rect.collidedict(d, use_values)

            self.assertIsNone(collide_item)

    def test_collidedict__barely_touching(self):
        """Ensures collidedict works correctly for rects that barely touch."""
        rect = Rect(1, 1, 10, 10)
        # Small rect to test barely touching collisions.
        collide_rect = Rect(0, 0, 1, 1)

        collide_item1 = ("collide 1", collide_rect)
        no_collide_item1 = ("no collide 1", Rect(50, 50, 10, 10))
        no_collide_item2 = ("no collide 2", Rect(60, 60, 10, 10))
        no_collide_item3 = ("no collide 3", Rect(70, 70, 10, 10))

        # Dict to check collisions with values.
        no_collide_rect_values = dict(
            (no_collide_item1, no_collide_item2, no_collide_item3)
        )

        # Dict to check collisions with keys.
        no_collide_rect_keys = {tuple(v): k for k, v in no_collide_rect_values.items()}

        # Tests the collide_rect on each of the rect's corners.
        for attr in ("topleft", "topright", "bottomright", "bottomleft"):
            setattr(collide_rect, attr, getattr(rect, attr))

            for use_values in (True, False):
                if use_values:
                    expected_item = collide_item1
                    d = dict(no_collide_rect_values)
                else:
                    expected_item = (tuple(collide_item1[1]), collide_item1[0])
                    d = dict(no_collide_rect_keys)

                d.update((expected_item,))  # Add in the expected item.

                collide_item = rect.collidedict(d, use_values)

                self.assertTupleEqual(collide_item, expected_item)

    def test_collidedict__zero_sized_rects(self):
        """Ensures collidedict works correctly with zero sized rects.

        There should be no collisions with zero sized rects.
        """
        zero_rect1 = Rect(1, 1, 0, 0)
        zero_rect2 = Rect(1, 1, 1, 0)
        zero_rect3 = Rect(1, 1, 0, 1)
        zero_rect4 = Rect(1, 1, -1, 0)
        zero_rect5 = Rect(1, 1, 0, -1)

        no_collide_item1 = ("no collide 1", zero_rect1.copy())
        no_collide_item2 = ("no collide 2", zero_rect2.copy())
        no_collide_item3 = ("no collide 3", zero_rect3.copy())
        no_collide_item4 = ("no collide 4", zero_rect4.copy())
        no_collide_item5 = ("no collide 5", zero_rect5.copy())
        no_collide_item6 = ("no collide 6", Rect(0, 0, 10, 10))
        no_collide_item7 = ("no collide 7", Rect(0, 0, 2, 2))

        # Dict to check collisions with values.
        rect_values = dict(
            (
                no_collide_item1,
                no_collide_item2,
                no_collide_item3,
                no_collide_item4,
                no_collide_item5,
                no_collide_item6,
                no_collide_item7,
            )
        )

        # Dict to check collisions with keys.
        rect_keys = {tuple(v): k for k, v in rect_values.items()}

        for use_values in (True, False):
            d = rect_values if use_values else rect_keys

            for zero_rect in (
                zero_rect1,
                zero_rect2,
                zero_rect3,
                zero_rect4,
                zero_rect5,
            ):
                collide_item = zero_rect.collidedict(d, use_values)

                self.assertIsNone(collide_item)

    def test_collidedict__zero_sized_rects_as_args(self):
        """Ensures collidedict works correctly with zero sized rects as args.

        There should be no collisions with zero sized rects.
        """
        rect = Rect(0, 0, 10, 10)

        no_collide_item1 = ("no collide 1", Rect(1, 1, 0, 0))
        no_collide_item2 = ("no collide 2", Rect(1, 1, 1, 0))
        no_collide_item3 = ("no collide 3", Rect(1, 1, 0, 1))

        # Dict to check collisions with values.
        rect_values = dict((no_collide_item1, no_collide_item2, no_collide_item3))

        # Dict to check collisions with keys.
        rect_keys = {tuple(v): k for k, v in rect_values.items()}

        for use_values in (True, False):
            d = rect_values if use_values else rect_keys

            collide_item = rect.collidedict(d, use_values)

            self.assertIsNone(collide_item)

    def test_collidedict__negative_sized_rects(self):
        """Ensures collidedict works correctly with negative sized rects."""
        neg_rect = Rect(1, 1, -1, -1)

        collide_item1 = ("collide 1", neg_rect.copy())
        collide_item2 = ("collide 2", Rect(0, 0, 10, 10))
        no_collide_item1 = ("no collide 1", Rect(1, 1, 10, 10))

        # Dict to check collisions with values.
        rect_values = dict((collide_item1, collide_item2, no_collide_item1))
        value_collide_items = (collide_item1, collide_item2)

        # Dict to check collisions with keys.
        rect_keys = {tuple(v): k for k, v in rect_values.items()}
        key_collide_items = tuple((tuple(v), k) for k, v in value_collide_items)

        for use_values in (True, False):
            if use_values:
                collide_items = value_collide_items
                d = rect_values
            else:
                collide_items = key_collide_items
                d = rect_keys

            collide_item = neg_rect.collidedict(d, use_values)

            # The detected collision could be any of the possible items.
            self.assertIn(collide_item, collide_items)

    def test_collidedict__negative_sized_rects_as_args(self):
        """Ensures collidedict works correctly with negative sized rect args."""
        rect = Rect(0, 0, 10, 10)

        collide_item1 = ("collide 1", Rect(1, 1, -1, -1))
        no_collide_item1 = ("no collide 1", Rect(1, 1, -1, 0))
        no_collide_item2 = ("no collide 2", Rect(1, 1, 0, -1))

        # Dict to check collisions with values.
        rect_values = dict((collide_item1, no_collide_item1, no_collide_item2))

        # Dict to check collisions with keys.
        rect_keys = {tuple(v): k for k, v in rect_values.items()}

        for use_values in (True, False):
            if use_values:
                expected_item = collide_item1
                d = rect_values
            else:
                expected_item = (tuple(collide_item1[1]), collide_item1[0])
                d = rect_keys

            collide_item = rect.collidedict(d, use_values)

            self.assertTupleEqual(collide_item, expected_item)

    def test_collidedict__invalid_dict_format(self):
        """Ensures collidedict correctly handles invalid dict parameters."""
        rect = Rect(0, 0, 10, 10)

        invalid_value_dict = ("collide", rect.copy())
        invalid_key_dict = tuple(invalid_value_dict[1]), invalid_value_dict[0]

        for use_values in (True, False):
            d = invalid_value_dict if use_values else invalid_key_dict

            with self.assertRaises(TypeError):
                collide_item = rect.collidedict(d, use_values)

    def test_collidedict__invalid_dict_value_format(self):
        """Ensures collidedict correctly handles dicts with invalid values."""
        rect = Rect(0, 0, 10, 10)
        rect_keys = {tuple(rect): "collide"}

        with self.assertRaises(TypeError):
            collide_item = rect.collidedict(rect_keys, 1)

    def test_collidedict__invalid_dict_key_format(self):
        """Ensures collidedict correctly handles dicts with invalid keys."""
        rect = Rect(0, 0, 10, 10)
        rect_values = {"collide": rect.copy()}

        with self.assertRaises(TypeError):
            collide_item = rect.collidedict(rect_values)

    def test_collidedict__invalid_use_values_format(self):
        """Ensures collidedict correctly handles invalid use_values parameters."""
        rect = Rect(0, 0, 1, 1)
        d = {}

        for invalid_param in (None, d, 1.1):
            with self.assertRaises(TypeError):
                collide_item = rect.collidedict(d, invalid_param)

    def test_collidedictall(self):
        """Ensures collidedictall detects collisions."""
        rect = Rect(1, 1, 10, 10)

        collide_item1 = ("collide 1", rect.copy())
        collide_item2 = ("collide 2", Rect(5, 5, 10, 10))
        no_collide_item1 = ("no collide 1", Rect(60, 60, 20, 20))
        no_collide_item2 = ("no collide 2", Rect(70, 70, 20, 20))

        # Dict to check collisions with values.
        rect_values = dict(
            (collide_item1, collide_item2, no_collide_item1, no_collide_item2)
        )
        value_collide_items = [collide_item1, collide_item2]

        # Dict to check collisions with keys.
        rect_keys = {tuple(v): k for k, v in rect_values.items()}
        key_collide_items = [(tuple(v), k) for k, v in value_collide_items]

        for use_values in (True, False):
            if use_values:
                expected_items = value_collide_items
                d = rect_values
            else:
                expected_items = key_collide_items
                d = rect_keys

            collide_items = rect.collidedictall(d, use_values)

            self._assertCountEqual(collide_items, expected_items)

    def test_collidedictall__no_collision(self):
        """Ensures collidedictall returns an empty list when no collisions."""
        rect = Rect(1, 1, 10, 10)

        no_collide_item1 = ("no collide 1", Rect(50, 50, 20, 20))
        no_collide_item2 = ("no collide 2", Rect(60, 60, 20, 20))
        no_collide_item3 = ("no collide 3", Rect(70, 70, 20, 20))

        # Dict to check collisions with values.
        rect_values = dict((no_collide_item1, no_collide_item2, no_collide_item3))

        # Dict to check collisions with keys.
        rect_keys = {tuple(v): k for k, v in rect_values.items()}

        expected_items = []

        for use_values in (True, False):
            d = rect_values if use_values else rect_keys

            collide_items = rect.collidedictall(d, use_values)

            self._assertCountEqual(collide_items, expected_items)

    def test_collidedictall__barely_touching(self):
        """Ensures collidedictall works correctly for rects that barely touch."""
        rect = Rect(1, 1, 10, 10)
        # Small rect to test barely touching collisions.
        collide_rect = Rect(0, 0, 1, 1)

        collide_item1 = ("collide 1", collide_rect)
        no_collide_item1 = ("no collide 1", Rect(50, 50, 20, 20))
        no_collide_item2 = ("no collide 2", Rect(60, 60, 20, 20))
        no_collide_item3 = ("no collide 3", Rect(70, 70, 20, 20))

        # Dict to check collisions with values.
        no_collide_rect_values = dict(
            (no_collide_item1, no_collide_item2, no_collide_item3)
        )

        # Dict to check collisions with keys.
        no_collide_rect_keys = {tuple(v): k for k, v in no_collide_rect_values.items()}

        # Tests the collide_rect on each of the rect's corners.
        for attr in ("topleft", "topright", "bottomright", "bottomleft"):
            setattr(collide_rect, attr, getattr(rect, attr))

            for use_values in (True, False):
                if use_values:
                    expected_items = [collide_item1]
                    d = dict(no_collide_rect_values)
                else:
                    expected_items = [(tuple(collide_item1[1]), collide_item1[0])]
                    d = dict(no_collide_rect_keys)

                d.update(expected_items)  # Add in the expected items.

                collide_items = rect.collidedictall(d, use_values)

                self._assertCountEqual(collide_items, expected_items)

    def test_collidedictall__zero_sized_rects(self):
        """Ensures collidedictall works correctly with zero sized rects.

        There should be no collisions with zero sized rects.
        """
        zero_rect1 = Rect(2, 2, 0, 0)
        zero_rect2 = Rect(2, 2, 2, 0)
        zero_rect3 = Rect(2, 2, 0, 2)
        zero_rect4 = Rect(2, 2, -2, 0)
        zero_rect5 = Rect(2, 2, 0, -2)

        no_collide_item1 = ("no collide 1", zero_rect1.copy())
        no_collide_item2 = ("no collide 2", zero_rect2.copy())
        no_collide_item3 = ("no collide 3", zero_rect3.copy())
        no_collide_item4 = ("no collide 4", zero_rect4.copy())
        no_collide_item5 = ("no collide 5", zero_rect5.copy())
        no_collide_item6 = ("no collide 6", Rect(0, 0, 10, 10))
        no_collide_item7 = ("no collide 7", Rect(0, 0, 2, 2))

        # Dict to check collisions with values.
        rect_values = dict(
            (
                no_collide_item1,
                no_collide_item2,
                no_collide_item3,
                no_collide_item4,
                no_collide_item5,
                no_collide_item6,
                no_collide_item7,
            )
        )

        # Dict to check collisions with keys.
        rect_keys = {tuple(v): k for k, v in rect_values.items()}

        expected_items = []

        for use_values in (True, False):
            d = rect_values if use_values else rect_keys

            for zero_rect in (
                zero_rect1,
                zero_rect2,
                zero_rect3,
                zero_rect4,
                zero_rect5,
            ):
                collide_items = zero_rect.collidedictall(d, use_values)

                self._assertCountEqual(collide_items, expected_items)

    def test_collidedictall__zero_sized_rects_as_args(self):
        """Ensures collidedictall works correctly with zero sized rects
        as args.

        There should be no collisions with zero sized rects.
        """
        rect = Rect(0, 0, 20, 20)

        no_collide_item1 = ("no collide 1", Rect(2, 2, 0, 0))
        no_collide_item2 = ("no collide 2", Rect(2, 2, 2, 0))
        no_collide_item3 = ("no collide 3", Rect(2, 2, 0, 2))

        # Dict to check collisions with values.
        rect_values = dict((no_collide_item1, no_collide_item2, no_collide_item3))

        # Dict to check collisions with keys.
        rect_keys = {tuple(v): k for k, v in rect_values.items()}

        expected_items = []

        for use_values in (True, False):
            d = rect_values if use_values else rect_keys

            collide_items = rect.collidedictall(d, use_values)

            self._assertCountEqual(collide_items, expected_items)

    def test_collidedictall__negative_sized_rects(self):
        """Ensures collidedictall works correctly with negative sized rects."""
        neg_rect = Rect(2, 2, -2, -2)

        collide_item1 = ("collide 1", neg_rect.copy())
        collide_item2 = ("collide 2", Rect(0, 0, 20, 20))
        no_collide_item1 = ("no collide 1", Rect(2, 2, 20, 20))

        # Dict to check collisions with values.
        rect_values = dict((collide_item1, collide_item2, no_collide_item1))
        value_collide_items = [collide_item1, collide_item2]

        # Dict to check collisions with keys.
        rect_keys = {tuple(v): k for k, v in rect_values.items()}
        key_collide_items = [(tuple(v), k) for k, v in value_collide_items]

        for use_values in (True, False):
            if use_values:
                expected_items = value_collide_items
                d = rect_values
            else:
                expected_items = key_collide_items
                d = rect_keys

            collide_items = neg_rect.collidedictall(d, use_values)

            self._assertCountEqual(collide_items, expected_items)

    def test_collidedictall__negative_sized_rects_as_args(self):
        """Ensures collidedictall works correctly with negative sized rect
        args.
        """
        rect = Rect(0, 0, 10, 10)

        collide_item1 = ("collide 1", Rect(1, 1, -1, -1))
        no_collide_item1 = ("no collide 1", Rect(1, 1, -1, 0))
        no_collide_item2 = ("no collide 2", Rect(1, 1, 0, -1))

        # Dict to check collisions with values.
        rect_values = dict((collide_item1, no_collide_item1, no_collide_item2))
        value_collide_items = [collide_item1]

        # Dict to check collisions with keys.
        rect_keys = {tuple(v): k for k, v in rect_values.items()}
        key_collide_items = [(tuple(v), k) for k, v in value_collide_items]

        for use_values in (True, False):
            if use_values:
                expected_items = value_collide_items
                d = rect_values
            else:
                expected_items = key_collide_items
                d = rect_keys

            collide_items = rect.collidedictall(d, use_values)

            self._assertCountEqual(collide_items, expected_items)

    def test_collidedictall__invalid_dict_format(self):
        """Ensures collidedictall correctly handles invalid dict parameters."""
        rect = Rect(0, 0, 10, 10)

        invalid_value_dict = ("collide", rect.copy())
        invalid_key_dict = tuple(invalid_value_dict[1]), invalid_value_dict[0]

        for use_values in (True, False):
            d = invalid_value_dict if use_values else invalid_key_dict

            with self.assertRaises(TypeError):
                collide_item = rect.collidedictall(d, use_values)

    def test_collidedictall__invalid_dict_value_format(self):
        """Ensures collidedictall correctly handles dicts with invalid values."""
        rect = Rect(0, 0, 10, 10)
        rect_keys = {tuple(rect): "collide"}

        with self.assertRaises(TypeError):
            collide_items = rect.collidedictall(rect_keys, 1)

    def test_collidedictall__invalid_dict_key_format(self):
        """Ensures collidedictall correctly handles dicts with invalid keys."""
        rect = Rect(0, 0, 10, 10)
        rect_values = {"collide": rect.copy()}

        with self.assertRaises(TypeError):
            collide_items = rect.collidedictall(rect_values)

    def test_collidedictall__invalid_use_values_format(self):
        """Ensures collidedictall correctly handles invalid use_values
        parameters.
        """
        rect = Rect(0, 0, 1, 1)
        d = {}

        for invalid_param in (None, d, 1.1):
            with self.assertRaises(TypeError):
                collide_items = rect.collidedictall(d, invalid_param)

    def test_collidelist(self):

        # __doc__ (as of 2008-08-02) for pygame.rect.Rect.collidelist:

        # Rect.collidelist(list): return index
        # test if one rectangle in a list intersects
        #
        # Test whether the rectangle collides with any in a sequence of
        # rectangles. The index of the first collision found is returned. If
        # no collisions are found an index of -1 is returned.

        r = Rect(1, 1, 10, 10)
        l = [Rect(50, 50, 1, 1), Rect(5, 5, 10, 10), Rect(15, 15, 1, 1)]

        self.assertEqual(r.collidelist(l), 1)

        f = [Rect(50, 50, 1, 1), (100, 100, 4, 4)]
        self.assertEqual(r.collidelist(f), -1)

    def test_collidelistall(self):

        # __doc__ (as of 2008-08-02) for pygame.rect.Rect.collidelistall:

        # Rect.collidelistall(list): return indices
        # test if all rectangles in a list intersect
        #
        # Returns a list of all the indices that contain rectangles that
        # collide with the Rect. If no intersecting rectangles are found, an
        # empty list is returned.

        r = Rect(1, 1, 10, 10)

        l = [
            Rect(1, 1, 10, 10),
            Rect(5, 5, 10, 10),
            Rect(15, 15, 1, 1),
            Rect(2, 2, 1, 1),
        ]
        self.assertEqual(r.collidelistall(l), [0, 1, 3])

        f = [Rect(50, 50, 1, 1), Rect(20, 20, 5, 5)]
        self.assertFalse(r.collidelistall(f))

    def test_fit(self):

        # __doc__ (as of 2008-08-02) for pygame.rect.Rect.fit:

        # Rect.fit(Rect): return Rect
        # resize and move a rectangle with aspect ratio
        #
        # Returns a new rectangle that is moved and resized to fit another.
        # The aspect ratio of the original Rect is preserved, so the new
        # rectangle may be smaller than the target in either width or height.

        r = Rect(10, 10, 30, 30)

        r2 = Rect(30, 30, 15, 10)

        f = r.fit(r2)
        self.assertTrue(r2.contains(f))

        f2 = r2.fit(r)
        self.assertTrue(r.contains(f2))

    def test_copy(self):
        r = Rect(1, 2, 10, 20)
        c = r.copy()
        self.assertEqual(c, r)

    def test_subscript(self):
        r = Rect(1, 2, 3, 4)
        self.assertEqual(r[0], 1)
        self.assertEqual(r[1], 2)
        self.assertEqual(r[2], 3)
        self.assertEqual(r[3], 4)
        self.assertEqual(r[-1], 4)
        self.assertEqual(r[-2], 3)
        self.assertEqual(r[-4], 1)
        self.assertRaises(IndexError, r.__getitem__, 5)
        self.assertRaises(IndexError, r.__getitem__, -5)
        self.assertEqual(r[0:2], [1, 2])
        self.assertEqual(r[0:4], [1, 2, 3, 4])
        self.assertEqual(r[0:-1], [1, 2, 3])
        self.assertEqual(r[:], [1, 2, 3, 4])
        self.assertEqual(r[...], [1, 2, 3, 4])
        self.assertEqual(r[0:4:2], [1, 3])
        self.assertEqual(r[0:4:3], [1, 4])
        self.assertEqual(r[3::-1], [4, 3, 2, 1])
        self.assertRaises(TypeError, r.__getitem__, None)

    def test_ass_subscript(self):
        r = Rect(0, 0, 0, 0)
        r[...] = 1, 2, 3, 4
        self.assertEqual(r, [1, 2, 3, 4])
        self.assertRaises(TypeError, r.__setitem__, None, 0)
        self.assertEqual(r, [1, 2, 3, 4])
        self.assertRaises(TypeError, r.__setitem__, 0, "")
        self.assertEqual(r, [1, 2, 3, 4])
        self.assertRaises(IndexError, r.__setitem__, 4, 0)
        self.assertEqual(r, [1, 2, 3, 4])
        self.assertRaises(IndexError, r.__setitem__, -5, 0)
        self.assertEqual(r, [1, 2, 3, 4])
        r[0] = 10
        self.assertEqual(r, [10, 2, 3, 4])
        r[3] = 40
        self.assertEqual(r, [10, 2, 3, 40])
        r[-1] = 400
        self.assertEqual(r, [10, 2, 3, 400])
        r[-4] = 100
        self.assertEqual(r, [100, 2, 3, 400])
        r[1:3] = 0
        self.assertEqual(r, [100, 0, 0, 400])
        r[...] = 0
        self.assertEqual(r, [0, 0, 0, 0])
        r[:] = 9
        self.assertEqual(r, [9, 9, 9, 9])
        r[:] = 11, 12, 13, 14
        self.assertEqual(r, [11, 12, 13, 14])
        r[::-1] = r
        self.assertEqual(r, [14, 13, 12, 11])


@unittest.skipIf(IS_PYPY, "fails on pypy")
class SubclassTest(unittest.TestCase):
    class MyRect(Rect):
        def __init__(self, *args, **kwds):
            super(SubclassTest.MyRect, self).__init__(*args, **kwds)
            self.an_attribute = True

    def test_copy(self):
        mr1 = self.MyRect(1, 2, 10, 20)
        self.assertTrue(mr1.an_attribute)
        mr2 = mr1.copy()
        self.assertTrue(isinstance(mr2, self.MyRect))
        self.assertRaises(AttributeError, getattr, mr2, "an_attribute")

    def test_move(self):
        mr1 = self.MyRect(1, 2, 10, 20)
        self.assertTrue(mr1.an_attribute)
        mr2 = mr1.move(1, 2)
        self.assertTrue(isinstance(mr2, self.MyRect))
        self.assertRaises(AttributeError, getattr, mr2, "an_attribute")

    def test_inflate(self):
        mr1 = self.MyRect(1, 2, 10, 20)
        self.assertTrue(mr1.an_attribute)
        mr2 = mr1.inflate(2, 4)
        self.assertTrue(isinstance(mr2, self.MyRect))
        self.assertRaises(AttributeError, getattr, mr2, "an_attribute")

    def test_clamp(self):
        mr1 = self.MyRect(19, 12, 5, 5)
        self.assertTrue(mr1.an_attribute)
        mr2 = mr1.clamp(Rect(10, 10, 10, 10))
        self.assertTrue(isinstance(mr2, self.MyRect))
        self.assertRaises(AttributeError, getattr, mr2, "an_attribute")

    def test_clip(self):
        mr1 = self.MyRect(1, 2, 3, 4)
        self.assertTrue(mr1.an_attribute)
        mr2 = mr1.clip(Rect(0, 0, 3, 4))
        self.assertTrue(isinstance(mr2, self.MyRect))
        self.assertRaises(AttributeError, getattr, mr2, "an_attribute")

    def test_union(self):
        mr1 = self.MyRect(1, 1, 1, 2)
        self.assertTrue(mr1.an_attribute)
        mr2 = mr1.union(Rect(-2, -2, 1, 2))
        self.assertTrue(isinstance(mr2, self.MyRect))
        self.assertRaises(AttributeError, getattr, mr2, "an_attribute")

    def test_unionall(self):
        mr1 = self.MyRect(0, 0, 1, 1)
        self.assertTrue(mr1.an_attribute)
        mr2 = mr1.unionall([Rect(-2, -2, 1, 1), Rect(2, 2, 1, 1)])
        self.assertTrue(isinstance(mr2, self.MyRect))
        self.assertRaises(AttributeError, getattr, mr2, "an_attribute")

    def test_fit(self):
        mr1 = self.MyRect(10, 10, 30, 30)
        self.assertTrue(mr1.an_attribute)
        mr2 = mr1.fit(Rect(30, 30, 15, 10))
        self.assertTrue(isinstance(mr2, self.MyRect))
        self.assertRaises(AttributeError, getattr, mr2, "an_attribute")


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