import unittest
import collections
from fontParts.base import FontPartsError
from .test_image import testImageData


class TestGlyph(unittest.TestCase):
    def getGlyph_generic(self):
        glyph, _ = self.objectGenerator("glyph")
        glyph.name = "Test Glyph 1"
        glyph.unicode = int(ord("X"))
        glyph.width = 250
        glyph.height = 750
        pen = glyph.getPen()
        pen.moveTo((100, -10))
        pen.lineTo((100, 100))
        pen.lineTo((200, 100))
        pen.lineTo((200, 0))
        pen.closePath()
        pen.moveTo((110, 10))
        pen.lineTo((110, 90))
        pen.lineTo((190, 90))
        pen.lineTo((190, 10))
        pen.closePath()
        glyph.appendAnchor("Test Anchor 1", (1, 2))
        glyph.appendAnchor("Test Anchor 2", (3, 4))
        glyph.appendGuideline((1, 2), 0, "Test Guideline 1")
        glyph.appendGuideline((3, 4), 90, "Test Guideline 2")
        return glyph

    def getGlyph_empty(self):
        glyph, _ = self.objectGenerator("glyph")
        glyph.name = "Test Glyph 2"
        glyph.unicode = int(ord("X"))
        glyph.width = 0
        glyph.height = 0
        return glyph

    def get_generic_object(self, obj_name):
        fp_object, _ = self.objectGenerator(obj_name)
        return fp_object

    # -------
    # Parents
    # -------

    def test_get_layer(self):
        font = self.get_generic_object("font")
        layer = font.layers[0]
        glyph = layer.newGlyph("A")
        self.assertEqual(glyph.layer, layer)

    def test_get_layer_orphan_glyph(self):
        glyph = self.get_generic_object("glyph")
        self.assertIsNone(glyph.layer)

    def test_get_font(self):
        font = self.get_generic_object("font")
        glyph = font.newGlyph("A")
        self.assertEqual(glyph.font, font)

    def test_get_font_orphan_glyph(self):
        glyph = self.get_generic_object("glyph")
        self.assertIsNone(glyph.font)

    # --------------
    # Identification
    # --------------

    def test_get_name(self):
        glyph = self.getGlyph_generic()
        self.assertEqual(glyph.name, "Test Glyph 1")

    def test_get_name_not_set(self):
        glyph = self.get_generic_object("glyph")
        self.assertIsNone(glyph.name)

    def test_set_name_valid(self):
        glyph = self.getGlyph_generic()
        name = "Test Glyph 1"  # the name is intentionally the same
        glyph.name = name
        self.assertEqual(glyph.name, name)

    def test_set_name_invalid(self):
        invalid_names = (
            ("", ValueError),
            ("A", ValueError),  # a glyph with this name already exists
            (3, TypeError),
            (None, TypeError),
        )
        font = self.get_generic_object("font")
        font.newGlyph("A")
        glyph = font.newGlyph("B")
        for name, err in invalid_names:
            with self.assertRaises(err):
                glyph.name = name

    def test_get_unicode(self):
        glyph = self.getGlyph_generic()
        self.assertEqual(glyph.unicode, 88)

    def test_get_unicode_not_set(self):
        glyph = self.get_generic_object("glyph")
        self.assertIsNone(glyph.unicode)

    def test_set_unicode_valid(self):
        valid_uni_values = (100, None, 0x6D, "6D")
        for value in valid_uni_values:
            glyph = self.get_generic_object("glyph")
            glyph.unicode = value
            result = int(value, 16) if isinstance(value, str) else value
            self.assertEqual(glyph.unicode, result)

    def test_set_unicode_value(self):
        glyph = self.get_generic_object("glyph")
        glyph.unicodes = (10, 20)
        glyph.unicode = 20
        self.assertEqual(glyph.unicodes, (20,))

    def test_set_unicode_value_none(self):
        glyph = self.get_generic_object("glyph")
        glyph.unicodes = (10, 20)
        glyph.unicode = None
        self.assertEqual(glyph.unicodes, ())

    def test_set_unicode_invalid(self):
        invalid_uni_values = (("GG", ValueError), (True, TypeError), ([], TypeError))
        glyph = self.get_generic_object("glyph")
        for value, err in invalid_uni_values:
            with self.assertRaises(err):
                glyph.unicode = value

    def test_get_unicodes(self):
        glyph = self.getGlyph_generic()
        self.assertEqual(glyph.unicodes, (88,))

    def test_get_unicodes_not_set(self):
        glyph = self.get_generic_object("glyph")
        self.assertEqual(glyph.unicodes, ())

    def test_set_unicodes_valid(self):
        valid_uni_values = ([100, 200], [], (300,), ())
        for values in valid_uni_values:
            glyph = self.get_generic_object("glyph")
            glyph.unicodes = values
            self.assertEqual(glyph.unicodes, tuple(values))

    def test_set_unicodes_invalid(self):
        invalid_uni_values = (("GG", ValueError), (True, TypeError), (30, TypeError))
        glyph = self.get_generic_object("glyph")
        for value, err in invalid_uni_values:
            with self.assertRaises(err):
                glyph.unicodes = value

    def test_set_unicodes_duplicates(self):
        glyph = self.get_generic_object("glyph")
        with self.assertRaises(ValueError):
            glyph.unicodes = (200, 110, 110)

    # -------
    # Metrics
    # -------

    # The methods are dynamically generated by test_generator()

    # Methods to test that empty glyphs have margins of None

    def test_get_leftMargin_not_set(self):
        glyph = self.get_generic_object("glyph")
        self.assertIsNone(glyph.leftMargin)

    def test_get_rightMargin_not_set(self):
        glyph = self.get_generic_object("glyph")
        self.assertIsNone(glyph.rightMargin)

    def test_get_bottomMargin_not_set(self):
        glyph = self.get_generic_object("glyph")
        self.assertIsNone(glyph.bottomMargin)

    def test_get_topMargin_not_set(self):
        glyph = self.get_generic_object("glyph")
        self.assertIsNone(glyph.topMargin)

    # -------
    # Queries
    # -------

    def test_get_bounds(self):
        glyph = self.getGlyph_generic()
        self.assertEqual(glyph.bounds, (100, -10, 200, 100))

    # ------
    # Layers
    # ------

    def test_get_layers(self):
        font = self.get_generic_object("font")
        glyph = font.newGlyph("A")
        layers = glyph.layers
        self.assertEqual(len(layers), 1)
        self.assertEqual(glyph.layer.name, font.defaultLayerName)
        self.assertEqual(
            layers[0],
            glyph,  # a glyph layer is really just a glyph
        )
        self.assertEqual(layers[0].name, "A")

    def test_get_layers_orphan_glyph(self):
        glyph = self.getGlyph_generic()
        self.assertEqual(glyph.layers, ())

    def test_getLayer_valid(self):
        font = self.get_generic_object("font")
        glyph = font.newGlyph("B")
        self.assertEqual(glyph.getLayer(font.defaultLayerName).name, "B")

    def test_getLayer_valid_not_found(self):
        font = self.get_generic_object("font")
        glyph = font.newGlyph("B")
        with self.assertRaises(ValueError):
            # No layer named 'layer_name' in glyph 'B'
            glyph.getLayer("layer_name")

    def test_getLayer_invalid(self):
        font = self.get_generic_object("font")
        glyph = font.newGlyph("B")
        with self.assertRaises(TypeError):
            glyph.getLayer()
        with self.assertRaises(TypeError):
            glyph.getLayer(None)
        with self.assertRaises(TypeError):
            glyph.getLayer(0)
        with self.assertRaises(ValueError):
            # Layer names must be at least one character long
            glyph.getLayer("")

    def test_newLayer_valid(self):
        font = self.get_generic_object("font")
        glyph = font.newGlyph("C")
        self.assertEqual(len(glyph.layers), 1)
        layer = glyph.newLayer("background")
        self.assertEqual(len(glyph.layers), 2)
        self.assertEqual(layer.name, "C")

    def test_newLayer_valid_already_exists(self):
        font = self.get_generic_object("font")
        glyph = font.newGlyph("C")
        self.assertEqual(len(glyph.layers), 1)
        glyph.newLayer("mask")
        self.assertEqual(len(glyph.layers), 2)
        glyph.newLayer("mask")  # intentional duplicate line
        self.assertEqual(len(glyph.layers), 2)

    def test_newLayer_invalid(self):
        font = self.get_generic_object("font")
        glyph = font.newGlyph("C")
        with self.assertRaises(TypeError):
            glyph.newLayer()
        with self.assertRaises(TypeError):
            glyph.newLayer(0)
        with self.assertRaises(ValueError):
            # Layer names must be at least one character long
            glyph.newLayer("")

    def test_removeLayer_valid_type_string(self):
        font = self.get_generic_object("font")
        glyph = font.newGlyph("D")
        self.assertEqual(len(glyph.layers), 1)
        glyph.removeLayer(font.defaultLayerName)
        self.assertEqual(len(glyph.layers), 0)

    def test_removeLayer_valid_type_glyph_layer(self):
        font = self.get_generic_object("font")
        glyph = font.newGlyph("D")
        self.assertEqual(len(glyph.layers), 1)
        glyph.removeLayer(glyph.layers[0])
        self.assertEqual(len(glyph.layers), 0)

    def test_removeLayer_valid_not_found_type_string(self):
        font = self.get_generic_object("font")
        glyph = font.newGlyph("D")
        with self.assertRaises(ValueError):
            # No layer named 'layer_name' in glyph 'D'
            glyph.removeLayer("layer_name")

    def test_removeLayer_invalid(self):
        font = self.get_generic_object("font")
        glyph = font.newGlyph("D")
        with self.assertRaises(TypeError):
            glyph.removeLayer()
        with self.assertRaises(TypeError):
            glyph.removeLayer(0)
        with self.assertRaises(ValueError):
            # Layer names must be at least one character long
            glyph.removeLayer("")

    # ------
    # Global
    # ------

    def test_clear(self):
        glyph = self.getGlyph_generic()
        glyph.appendComponent("component 1")
        self.assertEqual(len(glyph), 2)
        self.assertEqual(len(glyph.components), 1)
        self.assertEqual(len(glyph.anchors), 2)
        self.assertEqual(len(glyph.guidelines), 2)
        glyph.clear(
            contours=False,
            components=False,
            anchors=False,
            guidelines=False,
            image=False,
        )
        glyph.clear()
        self.assertEqual(len(glyph), 0)
        self.assertEqual(len(glyph.components), 0)
        self.assertEqual(len(glyph.anchors), 0)
        self.assertEqual(len(glyph.guidelines), 0)

    def test_appendGlyph(self):
        glyph_one = self.getGlyph_generic()
        glyph_two = self.getGlyph_generic()
        glyph_one.appendComponent("component 1")
        glyph_two.appendComponent("component 2")
        self.assertEqual(len(glyph_one), 2)
        self.assertEqual(len(glyph_one.components), 1)
        self.assertEqual(len(glyph_one.anchors), 2)
        self.assertEqual(len(glyph_one.guidelines), 2)
        glyph_one.appendGlyph(glyph_two)
        glyph_one.appendGlyph(glyph_two, (300, -40))
        self.assertEqual(len(glyph_one), 6)
        self.assertEqual(len(glyph_one.components), 3)
        self.assertEqual(len(glyph_one.anchors), 6)
        self.assertEqual(len(glyph_one.guidelines), 6)

    # --------
    # Contours
    # --------

    def test_get_contour_invalid(self):
        glyph = self.getGlyph_generic()
        with self.assertRaises(ValueError):
            # No contour located at index 5
            glyph[5]

    def test_insertContour_offset_valid(self):
        glyph = self.getGlyph_generic()
        contour = self.get_generic_object("contour")
        index = 1
        contour.insertPoint(0, position=(0, 0))
        contour.insertPoint(1, position=(100, 100))
        contour.insertPoint(2, position=(0, 100))

        self.assertEqual(len(glyph), 2)
        newcontour = glyph.insertContour(index, contour, (45, 50))
        self.assertEqual(len(glyph), 3)

        self.assertEqual(newcontour, glyph[index])
        self.assertEqual(len(newcontour.points), 3)
        self.assertEqual(newcontour.points[0].x, 45)
        self.assertEqual(newcontour.points[0].y, 50)

    def test_appendContour_offset_valid(self):
        glyph = self.getGlyph_generic()
        contour = self.get_generic_object("contour")
        contour.insertPoint(0, position=(0, 0))
        contour.insertPoint(1, position=(100, 100))
        contour.insertPoint(2, position=(0, 100))

        self.assertEqual(len(glyph), 2)
        newcontour = glyph.appendContour(contour, (45, 50))
        self.assertEqual(len(glyph), 3)

        self.assertEqual(newcontour, glyph[-1])
        self.assertEqual(len(newcontour.points), 3)
        self.assertEqual(newcontour.points[0].x, 45)
        self.assertEqual(newcontour.points[0].y, 50)

    def test_removeContour_valid(self):
        glyph = self.getGlyph_generic()
        contour = self.get_generic_object("contour")
        glyph.appendContour(contour)
        contour1 = glyph.contours[1]
        self.assertEqual(len(glyph), 3)
        glyph.removeContour(contour1)
        self.assertEqual(len(glyph), 2)
        glyph.removeContour(0)
        self.assertEqual(len(glyph), 1)

    def test_removeContour_invalid(self):
        glyph = self.getGlyph_generic()
        with self.assertRaises(ValueError):
            # No contour located at index 5
            glyph.removeContour(5)
        with self.assertRaises(FontPartsError):
            # The contour could not be found
            glyph.removeContour(self.get_generic_object("contour"))

    def test_clearContours(self):
        glyph = self.getGlyph_generic()
        self.assertEqual(len(glyph), 2)
        glyph.clearContours()
        self.assertEqual(len(glyph), 0)

    def test_autoContourOrder_points(self):
        glyph = self.getGlyph_empty()
        pen = glyph.getPen()
        pen.moveTo((287, 212))
        pen.lineTo((217, 108))
        pen.lineTo((407, 109))
        pen.closePath()

        pen = glyph.getPen()
        pen.moveTo((73, 184))
        pen.lineTo((39, 112))
        pen.lineTo((147, 61))
        pen.lineTo((140, 137))
        pen.lineTo((110, 176))
        pen.closePath()

        pen = glyph.getPen()
        pen.moveTo((60, 351))
        pen.lineTo((149, 421))
        pen.lineTo((225, 398))
        pen.lineTo((237, 290))
        pen.lineTo((183, 239))
        pen.lineTo((129, 245))
        pen.lineTo((70, 285))
        pen.closePath()

        glyph.autoContourOrder()
        self.assertEqual([len(c.points) for c in glyph.contours], [7, 5, 3])

    def test_autoContourOrder_segments(self):
        glyph = self.getGlyph_empty()

        pen = glyph.getPen()
        pen.moveTo((116, 202))
        pen.curveTo((116, 308), (156, 348), (245, 348))
        pen.closePath()

        pen = glyph.getPen()
        pen.moveTo((261, 212))
        pen.lineTo((335, 212))
        pen.lineTo((335, 301))
        pen.lineTo((261, 301))
        pen.closePath()

        glyph.autoContourOrder()
        self.assertEqual([len(c.segments) for c in glyph.contours], [4, 2])

    def test_autoContourOrder_fuzzycenter(self):
        glyph = self.getGlyph_empty()

        # the contours are overlapping too much
        # the different position of their center points
        # should not matter
        pen = glyph.getPen()
        pen.moveTo((313, 44))
        pen.curveTo((471, 44), (600, 174), (600, 332))
        pen.curveTo((600, 490), (471, 619), (313, 619))
        pen.curveTo((155, 619), (26, 490), (26, 332))
        pen.curveTo((26, 174), (155, 44), (313, 44))
        pen.closePath()

        pen = glyph.getPen()
        pen.moveTo((288, 122))
        pen.curveTo((383, 122), (461, 200), (461, 295))
        pen.curveTo((461, 390), (383, 468), (288, 468))
        pen.curveTo((192, 468), (114, 390), (114, 295))
        pen.curveTo((114, 200), (192, 122), (288, 122))
        pen.closePath()

        glyph.autoContourOrder()

        self.assertTrue(
            glyph.contours[0].points[0].x == 313
            and glyph.contours[1].points[0].x == 288
        )

    def test_autoContourOrder_distantCenter(self):
        glyph = self.getGlyph_empty()

        # both outlines have same structure
        # but their center points are far away
        # so, the center points should inform
        # the sorting
        # from left to right first, then bottom to top
        pen = glyph.getPen()
        pen.moveTo((313, 44))
        pen.curveTo((471, 44), (600, 174), (600, 332))
        pen.curveTo((600, 490), (471, 619), (313, 619))
        pen.curveTo((155, 619), (26, 490), (26, 332))
        pen.curveTo((26, 174), (155, 44), (313, 44))
        pen.closePath()

        pen = glyph.getPen()
        pen.moveTo((-142, -118))
        pen.curveTo((-47, -118), (31, -40), (31, 55))
        pen.curveTo((31, 150), (-47, 228), (-142, 228))
        pen.curveTo((-238, 228), (-316, 150), (-316, 55))
        pen.curveTo((-316, -40), (-238, -118), (-142, -118))
        pen.closePath()

        glyph.autoContourOrder()
        self.assertTrue(
            glyph.contours[0].points[0].x == -142
            and glyph.contours[1].points[0].x == 313
        )

    def test_autoContourOrder_bboxsurface(self):
        glyph = self.getGlyph_empty()

        # both outlines share the same center
        # so, the surface of the bounding box should inform
        # the sorting, from larger to smaller
        pen = glyph.getPen()
        pen.moveTo((100, 50))
        pen.curveTo((128, 50), (150, 72), (150, 100))
        pen.curveTo((150, 128), (128, 150), (100, 150))
        pen.curveTo((72, 150), (50, 128), (50, 100))
        pen.curveTo((50, 72), (72, 50), (100, 50))
        pen.closePath()

        pen = glyph.getPen()
        pen.moveTo((100, 0))
        pen.curveTo((155, 0), (200, 45), (200, 100))
        pen.curveTo((200, 155), (155, 200), (100, 200))
        pen.curveTo((45, 200), (0, 155), (0, 100))
        pen.curveTo((0, 45), (45, 0), (100, 0))
        pen.closePath()

        glyph.autoContourOrder()
        self.assertTrue(
            glyph.contours[0].points[0].y == 0 and glyph.contours[1].points[0].y == 50
        )

    # ----------
    # Components
    # ----------

    # appendComponent

    def test_appendComponent_invalid_circularReference(self):
        glyph = self.getGlyph_generic()
        with self.assertRaises(FontPartsError):
            glyph.appendComponent(glyph.name)

    def test_appendComponent_valid_object(self):
        glyph = self.getGlyph_generic()
        src, _ = self.objectGenerator("component")
        src.baseGlyph = "test"
        src.transformation = (1, 2, 3, 4, 5, 6)
        src.getIdentifier()
        dst = glyph.appendComponent(component=src)
        self.assertNotEqual(src, dst)
        self.assertEqual(src.baseGlyph, dst.baseGlyph)
        self.assertEqual(src.transformation, dst.transformation)
        self.assertEqual(src.identifier, dst.identifier)

    def test_appendComponent_valid_object_baseGlyph(self):
        glyph = self.getGlyph_generic()
        src, _ = self.objectGenerator("component")
        src.baseGlyph = "assigned"
        dst = glyph.appendComponent(component=src, baseGlyph="argument")
        self.assertEqual(dst.baseGlyph, "argument")

    def test_appendComponent_valid_object_offset(self):
        glyph = self.getGlyph_generic()
        src, _ = self.objectGenerator("component")
        src.baseGlyph = "test"
        src.transformation = (1, 2, 3, 4, 5, 6)
        dst = glyph.appendComponent(component=src, offset=(-1, -2))
        self.assertEqual(dst.offset, (-1, -2))
        self.assertEqual(dst.transformation, (1, 2, 3, 4, -1, -2))

    def test_appendComponent_valid_object_scale(self):
        glyph = self.getGlyph_generic()
        src, _ = self.objectGenerator("component")
        src.baseGlyph = "test"
        src.transformation = (1, 2, 3, 4, 5, 6)
        dst = glyph.appendComponent(component=src, scale=(-1, -2))
        self.assertEqual(dst.scale, (-1, -2))
        self.assertEqual(dst.transformation, (-1, 2, 3, -2, 5, 6))

    def test_appendComponent_valid_object_conflictingIdentifier(self):
        glyph = self.getGlyph_generic()
        c = glyph.appendComponent("test")
        existingIdentifier = c.getIdentifier()
        src, _ = self.objectGenerator("component")
        src.baseGlyph = "test"
        src._setIdentifier(existingIdentifier)
        dst = glyph.appendComponent(component=src)
        self.assertNotEqual(src.identifier, dst.identifier)

    def test_appendComponent_invalid_object_circularReference(self):
        glyph = self.getGlyph_generic()
        src, _ = self.objectGenerator("component")
        src.baseGlyph = glyph.name
        with self.assertRaises(FontPartsError):
            glyph.appendComponent(glyph.name)

    # removeComponent

    def test_removeComponent_valid(self):
        glyph = self.getGlyph_generic()
        glyph.appendComponent("component 1")
        glyph.appendComponent("component 2")
        glyph.appendComponent("component 3")
        self.assertEqual(len(glyph.components), 3)
        component = glyph.components[1]
        glyph.removeComponent(component)
        self.assertEqual(len(glyph.components), 2)
        glyph.removeComponent(0)
        self.assertEqual(len(glyph.components), 1)

    def test_removeComponent_invalid(self):
        glyph = self.getGlyph_generic()
        with self.assertRaises(ValueError):
            # No component located at index 8
            glyph.removeComponent(8)
        with self.assertRaises(FontPartsError):
            # The component could not be found
            glyph.removeComponent(self.get_generic_object("component"))

    # clearComponents

    def test_clearComponents(self):
        glyph = self.getGlyph_generic()
        glyph.appendComponent("component 1")
        self.assertEqual(len(glyph.components), 1)
        glyph.clearComponents()
        self.assertEqual(len(glyph.components), 0)

    # -------
    # Anchors
    # -------

    # appendAnchor

    def test_appendAnchor_valid_object(self):
        glyph = self.getGlyph_generic()
        src, _ = self.objectGenerator("anchor")
        src.name = "test"
        src.position = (1, 2)
        src.color = (1, 1, 1, 1)
        src.getIdentifier()
        dst = glyph.appendAnchor(anchor=src)
        self.assertNotEqual(src, dst)
        self.assertEqual(src.name, dst.name)
        self.assertEqual(src.position, dst.position)
        self.assertEqual(src.color, dst.color)
        self.assertEqual(src.identifier, dst.identifier)

    # removeAnchor

    def test_removeAnchor_valid(self):
        glyph = self.getGlyph_generic()
        glyph.appendAnchor("base", (250, 0), (1, 0, 0, 0.5))
        anchor = glyph.anchors[1]
        self.assertEqual(len(glyph.anchors), 3)
        glyph.removeAnchor(anchor)
        self.assertEqual(len(glyph.anchors), 2)
        glyph.removeAnchor(0)
        self.assertEqual(len(glyph.anchors), 1)

    def test_removeAnchor_invalid(self):
        glyph = self.getGlyph_generic()
        with self.assertRaises(ValueError):
            # No anchor located at index 4
            glyph.removeAnchor(4)
        with self.assertRaises(FontPartsError):
            # The anchor could not be found
            glyph.removeAnchor(self.get_generic_object("anchor"))

    # clearAnchors

    def test_clearAnchors(self):
        glyph = self.getGlyph_generic()
        self.assertEqual(len(glyph.anchors), 2)
        glyph.clearAnchors()
        self.assertEqual(len(glyph.anchors), 0)

    # ----------
    # Guidelines
    # ----------

    # appendGuideline

    def test_appendGuideline_valid_object(self):
        glyph = self.getGlyph_generic()
        src, _ = self.objectGenerator("guideline")
        src.position = (1, 2)
        src.angle = 123
        src.name = "test"
        src.color = (1, 1, 1, 1)
        src.getIdentifier()
        dst = glyph.appendGuideline(guideline=src)
        self.assertNotEqual(src, dst)
        self.assertEqual(src.position, dst.position)
        self.assertEqual(src.angle, dst.angle)
        self.assertEqual(src.name, dst.name)
        self.assertEqual(src.color, dst.color)
        self.assertEqual(src.identifier, dst.identifier)

    # removeGuideline

    def test_removeGuideline_valid(self):
        glyph = self.getGlyph_generic()
        glyph.appendGuideline((5, -10), 90, None, (1, 0, 0, 0.5))
        guideline = glyph.guidelines[1]
        self.assertEqual(len(glyph.guidelines), 3)
        glyph.removeGuideline(guideline)
        self.assertEqual(len(glyph.guidelines), 2)
        glyph.removeGuideline(0)
        self.assertEqual(len(glyph.guidelines), 1)

    def test_removeGuideline_invalid(self):
        glyph = self.getGlyph_generic()
        with self.assertRaises(ValueError):
            # No guideline located at index 6
            glyph.removeGuideline(6)
        with self.assertRaises(FontPartsError):
            # The guideline could not be found
            glyph.removeGuideline(self.get_generic_object("guideline"))

    # clearGuidelines

    def test_clearGuidelines(self):
        glyph = self.getGlyph_generic()
        self.assertEqual(len(glyph.guidelines), 2)
        glyph.clearGuidelines()
        self.assertEqual(len(glyph.guidelines), 0)

    # -----
    # Image
    # -----

    def test_addImage(self):
        font = self.get_generic_object("font")
        glyph = font.newGlyph("glyphWithImage")
        image = glyph.addImage(data=testImageData)
        self.assertEqual(image.data, testImageData)

    # ----
    # Hash
    # ----

    def test_hash_object_self(self):
        glyph_one = self.getGlyph_generic()
        glyph_one.name = "Test"
        self.assertEqual(hash(glyph_one), hash(glyph_one))

    def test_hash_object_other(self):
        glyph_one = self.getGlyph_generic()
        glyph_two = self.getGlyph_generic()
        glyph_one.name = "Test"
        glyph_two.name = "Test"
        self.assertNotEqual(hash(glyph_one), hash(glyph_two))

    def test_hash_object_self_variable_assignment(self):
        glyph_one = self.getGlyph_generic()
        a = glyph_one
        self.assertEqual(hash(glyph_one), hash(a))

    def test_hash_object_other_variable_assignment(self):
        glyph_one = self.getGlyph_generic()
        glyph_two = self.getGlyph_generic()
        a = glyph_one
        self.assertNotEqual(hash(glyph_two), hash(a))

    def test_is_hashable(self):
        glyph_one = self.getGlyph_generic()
        self.assertTrue(isinstance(glyph_one, collections.abc.Hashable))

    # --------
    # Equality
    # --------

    def test_object_equal_self(self):
        glyph_one = self.getGlyph_generic()
        glyph_one.name = "Test"
        self.assertEqual(glyph_one, glyph_one)

    def test_object_not_equal_other(self):
        glyph_one = self.getGlyph_generic()
        glyph_two = self.getGlyph_generic()
        self.assertNotEqual(glyph_one, glyph_two)

    def test_object_not_equal_other_name_same(self):
        glyph_one = self.getGlyph_generic()
        glyph_two = self.getGlyph_generic()
        glyph_one.name = "Test"
        glyph_two.name = "Test"
        self.assertNotEqual(glyph_one, glyph_two)

    def test_object_equal_variable_assignment(self):
        glyph_one = self.getGlyph_generic()
        a = glyph_one
        a.name = "Other"
        self.assertEqual(glyph_one, a)

    def test_object_not_equal_variable_assignment(self):
        glyph_one = self.getGlyph_generic()
        glyph_two = self.getGlyph_generic()
        a = glyph_one
        self.assertNotEqual(glyph_two, a)

    # ---------
    # Selection
    # ---------

    def test_selected_true(self):
        glyph = self.getGlyph_generic()
        try:
            glyph.selected = False
        except NotImplementedError:
            return
        glyph.selected = True
        self.assertTrue(glyph.selected)

    def test_not_selected_false(self):
        glyph = self.getGlyph_generic()
        try:
            glyph.selected = False
        except NotImplementedError:
            return
        self.assertFalse(glyph.selected)

    # Contours

    def test_selectedContours_default(self):
        glyph = self.getGlyph_generic()
        contour1 = glyph.contours[0]
        try:
            contour1.selected = False
        except NotImplementedError:
            return
        self.assertEqual(glyph.selectedContours, ())

    def test_selectedContours_setSubObject(self):
        glyph = self.getGlyph_generic()
        contour1 = glyph.contours[0]
        contour2 = glyph.contours[1]
        try:
            contour1.selected = False
        except NotImplementedError:
            return
        contour2.selected = True
        self.assertEqual(glyph.selectedContours, (contour2,))

    def test_selectedContours_setFilledList(self):
        glyph = self.getGlyph_generic()
        contour1 = glyph.contours[0]
        contour2 = glyph.contours[1]
        try:
            contour1.selected = False
        except NotImplementedError:
            return
        glyph.selectedContours = [contour1, contour2]
        self.assertEqual(glyph.selectedContours, (contour1, contour2))

    def test_selectedContours_setEmptyList(self):
        glyph = self.getGlyph_generic()
        contour1 = glyph.contours[0]
        try:
            contour1.selected = True
        except NotImplementedError:
            return
        glyph.selectedContours = []
        self.assertEqual(glyph.selectedContours, ())

    # Components
    def test_selectedComponents_default(self):
        glyph = self.getGlyph_generic()
        glyph.appendComponent("component 1")
        component1 = glyph.components[0]
        try:
            component1.selected = False
        except NotImplementedError:
            return
        self.assertEqual(glyph.selectedComponents, ())

    def test_selectedComponents_setSubObject(self):
        glyph = self.getGlyph_generic()
        glyph.appendComponent("component 1")
        glyph.appendComponent("component 2")
        component1 = glyph.components[0]
        component2 = glyph.components[1]
        try:
            component1.selected = False
        except NotImplementedError:
            return
        component2.selected = True
        self.assertEqual(glyph.selectedComponents, (component2,))

    def test_selectedComponents_setFilledList(self):
        glyph = self.getGlyph_generic()
        glyph.appendComponent("component 1")
        glyph.appendComponent("component 2")
        component1 = glyph.components[0]
        component2 = glyph.components[1]
        try:
            component1.selected = False
        except NotImplementedError:
            return
        glyph.selectedComponents = [component1, component2]
        self.assertEqual(glyph.selectedComponents, (component1, component2))

    def test_selectedComponents_setEmptyList(self):
        glyph = self.getGlyph_generic()
        glyph.appendComponent("component 1")
        component1 = glyph.components[0]
        try:
            component1.selected = True
        except NotImplementedError:
            return
        glyph.selectedComponents = []
        self.assertEqual(glyph.selectedComponents, ())

    # Anchors

    def test_selectedAnchors_default(self):
        glyph = self.getGlyph_generic()
        anchor1 = glyph.anchors[0]
        try:
            anchor1.selected = False
        except NotImplementedError:
            return
        self.assertEqual(glyph.selectedAnchors, ())

    def test_selectedAnchors_setSubObject(self):
        glyph = self.getGlyph_generic()
        anchor1 = glyph.anchors[0]
        anchor2 = glyph.anchors[1]
        try:
            anchor1.selected = False
        except NotImplementedError:
            return
        anchor2.selected = True
        self.assertEqual(glyph.selectedAnchors, (anchor2,))

    def test_selectedAnchors_setFilledList(self):
        glyph = self.getGlyph_generic()
        anchor1 = glyph.anchors[0]
        anchor2 = glyph.anchors[1]
        try:
            anchor1.selected = False
        except NotImplementedError:
            return
        glyph.selectedAnchors = [anchor1, anchor2]
        self.assertEqual(glyph.selectedAnchors, (anchor1, anchor2))

    def test_selectedAnchors_setEmptyList(self):
        glyph = self.getGlyph_generic()
        anchor1 = glyph.anchors[0]
        try:
            anchor1.selected = True
        except NotImplementedError:
            return
        glyph.selectedAnchors = []
        self.assertEqual(glyph.selectedAnchors, ())

    # Guidelines

    def test_selectedGuidelines_default(self):
        glyph = self.getGlyph_generic()
        guideline1 = glyph.guidelines[0]
        try:
            guideline1.selected = False
        except NotImplementedError:
            return
        self.assertEqual(glyph.selectedGuidelines, ())

    def test_selectedGuidelines_setSubObject(self):
        glyph = self.getGlyph_generic()
        guideline1 = glyph.guidelines[0]
        guideline2 = glyph.guidelines[1]
        try:
            guideline1.selected = False
        except NotImplementedError:
            return
        guideline2.selected = True
        self.assertEqual(glyph.selectedGuidelines, (guideline2,))

    def test_selectedGuidelines_setFilledList(self):
        glyph = self.getGlyph_generic()
        guideline1 = glyph.guidelines[0]
        guideline2 = glyph.guidelines[1]
        try:
            guideline1.selected = False
        except NotImplementedError:
            return
        glyph.selectedGuidelines = [guideline1, guideline2]
        self.assertEqual(glyph.selectedGuidelines, (guideline1, guideline2))

    def test_selectedGuidelines_setEmptyList(self):
        glyph = self.getGlyph_generic()
        guideline1 = glyph.guidelines[0]
        try:
            guideline1.selected = True
        except NotImplementedError:
            return
        glyph.selectedGuidelines = []
        self.assertEqual(glyph.selectedGuidelines, ())

    # -------------
    # Compatibility
    # -------------

    def test_isCompatible_anchors(self):
        glyph1 = self.getGlyph_generic()
        glyph1.clearAnchors()
        glyph1.appendAnchor("a", (0, 0))
        glyph1.appendAnchor("b", (0, 0))
        glyph2 = self.getGlyph_generic()
        glyph2.clearAnchors()
        glyph2.appendAnchor("a", (0, 0))
        glyph2.appendAnchor("b", (0, 0))
        is_compatible, report = glyph1.isCompatible(glyph2)
        self.assertTrue(is_compatible)
        self.assertFalse(report.anchorDifferences)
        self.assertFalse(report.anchorOrderDifference)
        self.assertFalse(report.anchorCountDifference)
        self.assertFalse(report.anchorsMissingFromGlyph1)
        self.assertFalse(report.anchorsMissingFromGlyph2)

    def test_isCompatible_anchors_order(self):
        glyph1 = self.getGlyph_generic()
        glyph1.clearAnchors()
        glyph1.appendAnchor("a", (0, 0))
        glyph1.appendAnchor("b", (0, 0))
        glyph2 = self.getGlyph_generic()
        glyph2.clearAnchors()
        glyph2.appendAnchor("b", (0, 0))
        glyph2.appendAnchor("a", (0, 0))
        is_compatible, report = glyph1.isCompatible(glyph2)
        self.assertTrue(is_compatible)
        self.assertEqual(report.anchorDifferences, [(0, "a", "b"), (1, "b", "a")])
        self.assertTrue(report.anchorOrderDifference)
        self.assertFalse(report.anchorCountDifference)
        self.assertFalse(report.anchorsMissingFromGlyph1)
        self.assertFalse(report.anchorsMissingFromGlyph2)

    def test_isCompatible_anchors_intersecting(self):
        glyph1 = self.getGlyph_generic()
        glyph1.clearAnchors()
        glyph1.appendAnchor("a", (0, 0))
        glyph1.appendAnchor("b", (0, 0))
        glyph2 = self.getGlyph_generic()
        glyph2.clearAnchors()
        glyph2.appendAnchor("a", (0, 0))
        glyph2.appendAnchor("b", (0, 0))
        glyph2.appendAnchor("b", (0, 0))
        is_compatible, report = glyph1.isCompatible(glyph2)
        self.assertTrue(is_compatible)
        self.assertEqual(report.anchorDifferences, [(2, None, "b")])
        self.assertFalse(report.anchorOrderDifference)
        self.assertTrue(report.anchorCountDifference)
        self.assertEqual(report.anchorsMissingFromGlyph1, ["b"])
        self.assertFalse(report.anchorsMissingFromGlyph2)

    def test_isCompatible_anchors_disjoint(self):
        glyph1 = self.getGlyph_generic()
        glyph1.clearAnchors()
        glyph1.appendAnchor("x", (0, 0))
        glyph2 = self.getGlyph_generic()
        glyph2.clearAnchors()
        glyph2.appendAnchor("a", (0, 0))
        glyph2.appendAnchor("a", (0, 0))
        glyph2.appendAnchor("b", (0, 0))
        is_compatible, report = glyph1.isCompatible(glyph2)
        self.assertTrue(is_compatible)
        self.assertEqual(
            report.anchorDifferences, [(0, "x", "a"), (1, None, "a"), (2, None, "b")]
        )
        self.assertFalse(report.anchorOrderDifference)
        self.assertTrue(report.anchorCountDifference)
        self.assertEqual(report.anchorsMissingFromGlyph1, ["a", "a", "b"])
        self.assertEqual(report.anchorsMissingFromGlyph2, ["x"])

    def test_isCompatible_components(self):
        glyph1, _ = self.objectGenerator("glyph")
        glyph1.appendComponent("a")
        glyph1.appendComponent("b")
        glyph2, _ = self.objectGenerator("glyph")
        glyph2.appendComponent("a")
        glyph2.appendComponent("b")
        is_compatible, report = glyph1.isCompatible(glyph2)
        self.assertTrue(is_compatible)
        self.assertFalse(report.componentDifferences)
        self.assertFalse(report.componentOrderDifference)
        self.assertFalse(report.componentCountDifference)
        self.assertFalse(report.componentsMissingFromGlyph1)
        self.assertFalse(report.componentsMissingFromGlyph2)

    def test_isCompatible_components_order(self):
        glyph1, _ = self.objectGenerator("glyph")
        glyph1.appendComponent("a")
        glyph1.appendComponent("b")
        glyph2, _ = self.objectGenerator("glyph")
        glyph2.appendComponent("b")
        glyph2.appendComponent("a")
        is_compatible, report = glyph1.isCompatible(glyph2)
        self.assertTrue(is_compatible)
        self.assertEqual(report.componentDifferences, [(0, "a", "b"), (1, "b", "a")])
        self.assertTrue(report.componentOrderDifference)
        self.assertFalse(report.componentCountDifference)
        self.assertFalse(report.componentsMissingFromGlyph1)
        self.assertFalse(report.componentsMissingFromGlyph2)

    def test_isCompatible_components_intersecting(self):
        glyph1, _ = self.objectGenerator("glyph")
        glyph1.appendComponent("a")
        glyph1.appendComponent("b")
        glyph2, _ = self.objectGenerator("glyph")
        glyph2.appendComponent("a")
        glyph2.appendComponent("b")
        glyph2.appendComponent("b")
        is_compatible, report = glyph1.isCompatible(glyph2)
        self.assertFalse(is_compatible)
        self.assertEqual(report.componentDifferences, [(2, None, "b")])
        self.assertFalse(report.componentOrderDifference)
        self.assertTrue(report.componentCountDifference)
        self.assertEqual(report.componentsMissingFromGlyph1, ["b"])
        self.assertFalse(report.componentsMissingFromGlyph2)

    def test_isCompatible_components_disjoint(self):
        glyph1, _ = self.objectGenerator("glyph")
        glyph1.appendComponent("x")
        glyph2, _ = self.objectGenerator("glyph")
        glyph2.appendComponent("a")
        glyph2.appendComponent("a")
        glyph2.appendComponent("b")
        is_compatible, report = glyph1.isCompatible(glyph2)
        self.assertFalse(is_compatible)
        self.assertEqual(
            report.componentDifferences, [(0, "x", "a"), (1, None, "a"), (2, None, "b")]
        )
        self.assertFalse(report.componentOrderDifference)
        self.assertTrue(report.componentCountDifference)
        self.assertEqual(report.componentsMissingFromGlyph1, ["a", "a", "b"])
        self.assertEqual(report.componentsMissingFromGlyph2, ["x"])

    def test_isCompatible_components_disjoint_equal_size(self):
        glyph1, _ = self.objectGenerator("glyph")
        glyph1.appendComponent("x")
        glyph1.appendComponent("y")
        glyph2, _ = self.objectGenerator("glyph")
        glyph2.appendComponent("a")
        glyph2.appendComponent("b")
        is_compatible, report = glyph1.isCompatible(glyph2)
        self.assertFalse(is_compatible)
        self.assertEqual(report.componentDifferences, [(0, "x", "a"), (1, "y", "b")])
        self.assertFalse(report.componentOrderDifference)
        self.assertFalse(report.componentCountDifference)
        self.assertEqual(report.componentsMissingFromGlyph1, ["a", "b"])
        self.assertEqual(report.componentsMissingFromGlyph2, ["x", "y"])

    # -------------
    # Interpolation
    # -------------

    def test_interpolate_glyphWidth_without_rounding(self):
        interpolated, _ = self.objectGenerator("glyph")
        glyph_min, _ = self.objectGenerator("glyph")
        glyph_max, _ = self.objectGenerator("glyph")
        glyph_min.width = 1000
        glyph_max.width = 2000
        interpolated.interpolate(0.5154, glyph_min, glyph_max, round=False)
        self.assertEqual(interpolated.width, 1515.4)

    def test_interpolate_glyphWidth_with_rounding(self):
        interpolated, _ = self.objectGenerator("glyph")
        glyph_min, _ = self.objectGenerator("glyph")
        glyph_max, _ = self.objectGenerator("glyph")
        glyph_min.width = 1000
        glyph_max.width = 2000
        interpolated.interpolate(0.5154, glyph_min, glyph_max, round=True)
        self.assertEqual(interpolated.width, 1515)

    # ---------------
    # Transformations
    # ---------------

    def test_moveBy_only_contours(self):
        glyph = self.getGlyph_generic()
        glyph.moveBy((100, 0))
        self.assertEqual(glyph.bounds[0], 200)
        glyph.moveBy((0, 250))
        self.assertEqual(glyph.bounds[1], 240)

    # ---
    # API
    # ---

    def test_isEmpty_false_outlines(self):
        glyph = self.getGlyph_generic()
        self.assertFalse(glyph.isEmpty())

    def test_isEmpty_true_clear(self):
        glyph = self.getGlyph_generic()
        glyph.clear()
        self.assertTrue(glyph.isEmpty())

    def test_isEmpty_false_component(self):
        glyph = self.getGlyph_generic()
        glyph.clear()
        glyph.appendComponent("component 1")
        self.assertFalse(glyph.isEmpty())

    def test_removeOverlap(self):
        glyph = self.getGlyph_generic()
        self.assertEqual(len(glyph), 2)
        glyph.removeOverlap()
        self.assertEqual(len(glyph), 1)


def test_generator(test_name, metric, value):
    if "_invalid_" in test_name:

        def test(self):
            glyph = self.getGlyph_generic()
            with self.assertRaises(TypeError):
                setattr(glyph, metric, value)
    else:

        def test(self):
            glyph = self.getGlyph_generic()
            if "_set_" in test_name:
                setattr(glyph, metric, value)
            self.assertEqual(getattr(glyph, metric), value)

    return test


t_names = (
    "_get",
    "_set_valid_positive",
    "_set_valid_negative",
    "_set_valid_zero",
    "_set_valid_float",
    "_set_invalid_string",
    "_set_invalid_none",
)
invalid = ("abc", None)
metrics = {
    "width": (250, 300, -485, 0, 101.5) + invalid,
    "height": (750, 800, -10, 0, 801.5) + invalid,
    "leftMargin": (100, 200, -15, 0, 201.5) + invalid,
    "rightMargin": (50, 80, -20, 0, 81.5) + invalid,
    "bottomMargin": (-10, 150, -35, 0, 151.5) + invalid,
    "topMargin": (650, 750, -250, 0, 751.5) + invalid,
}

for i, t_name_suffix in enumerate(t_names):
    for metric_name, values in metrics.items():
        test_name = f"test_{metric_name}{t_name_suffix}"
        test = test_generator(test_name, metric_name, values[i])
        setattr(TestGlyph, test_name, test)
