1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
|
"""Tests for utility functions that ufo2ft provides."""
import re
import pytest
from ufo2ft import util
from ufo2ft.errors import InvalidFontData
from ufo2ft.util import zip_strict
def test_overloaded_mapping_raises_error(FontClass):
"""Test that util.makeUnicodeToGlyphNameMapping() raises an error when
multiple glyphs are mapped to the same codepoint."""
# Make an empty font in memory with glyphs 'A' and 'B'.
test_ufo = FontClass()
glyph_a = test_ufo.newGlyph("A")
glyph_b = test_ufo.newGlyph("B")
# Test that the util function DOES NOT raise an error when the glyphs are
# mapped to distinct codepoints, and that the function returns the correct
# mapping.
glyph_a.unicodes = [0x0041]
glyph_b.unicodes = [0x0042]
assert util.makeUnicodeToGlyphNameMapping(test_ufo) == {0x0041: "A", 0x0042: "B"}
# Test that the util function DOES raise an error when multiple glyphs are
# mapped to the same codepoint, and that this error is generally
# descriptive.
glyph_a.unicodes = [0x0041]
glyph_b.unicodes = [0x0041]
with pytest.raises(
InvalidFontData,
match=re.escape("cannot map 'B' to U+0041; already mapped to 'A'"),
):
util.makeUnicodeToGlyphNameMapping(test_ufo)
def test_getMaxComponentDepth_cyclical_reference():
# ufoLib2 lets you create cyclical component references (defcon would fail with
# RecursionError while creating them so we don't test it below).
# Here we test that we properly detect them and provide a descriptive error message.
# https://github.com/googlefonts/fontmake/issues/1066
test_ufo = pytest.importorskip("ufoLib2").Font()
glyph_a = test_ufo.newGlyph("A")
glyph_b = test_ufo.newGlyph("B")
glyph_c = test_ufo.newGlyph("C")
glyph_a.getPen().addComponent("C", (1, 0, 0, 1, 0, 0))
glyph_b.getPen().addComponent("A", (1, 0, 0, 1, 0, 0))
glyph_c.getPen().addComponent("B", (1, 0, 0, 1, 0, 0))
with pytest.raises(
InvalidFontData, match="cyclical component reference: A -> C -> B => A"
):
util.getMaxComponentDepth(glyph_a, test_ufo)
with pytest.raises(
InvalidFontData, match="cyclical component reference: B -> A -> C => B"
):
util.getMaxComponentDepth(glyph_b, test_ufo)
with pytest.raises(
InvalidFontData, match="cyclical component reference: C -> B -> A => C"
):
util.getMaxComponentDepth(glyph_c, test_ufo)
glyph_d = test_ufo.newGlyph("D")
glyph_e = test_ufo.newGlyph("E")
glyph_f = test_ufo.newGlyph("F")
glyph_g = test_ufo.newGlyph("G")
glyph_h = test_ufo.newGlyph("H")
# adding same component multiple times should not cause infinite recursion
glyph_d.getPen().addComponent("E", (1, 0, 0, 1, 0, 0))
glyph_d.getPen().addComponent("E", (1, 0, 0, 1, 0, 0))
# G is reachable from both E and F, but there is no cycle.
glyph_e.getPen().addComponent("F", (1, 0, 0, 1, 0, 0))
glyph_f.getPen().addComponent("G", (1, 0, 0, 1, 0, 0))
glyph_e.getPen().addComponent("G", (1, 0, 0, 1, 0, 0))
glyph_g.getPen().addComponent("H", (1, 0, 0, 1, 0, 0))
assert util.getMaxComponentDepth(glyph_d, test_ufo) == 4
assert util.getMaxComponentDepth(glyph_e, test_ufo) == 3
assert util.getMaxComponentDepth(glyph_f, test_ufo) == 2
assert util.getMaxComponentDepth(glyph_g, test_ufo) == 1
assert util.getMaxComponentDepth(glyph_h, test_ufo) == 0
def test_zip_strict():
assert list(zip_strict([0, 1], [2, 3])) == [(0, 2), (1, 3)]
with pytest.raises(
ValueError, match=r"zip\(\) argument 2 is shorter than argument 1"
):
list(zip_strict([0, 1, 2], [3, 4]))
with pytest.raises(
ValueError, match=r"zip\(\) argument 3 is shorter than arguments 1-2"
):
list(zip_strict([0, 1, 2], [3, 4], [5]))
with pytest.raises(
ValueError, match=r"zip\(\) argument 2 is longer than argument 1"
):
list(zip_strict([0], [1, 2]))
with pytest.raises(
ValueError, match=r"zip\(\) argument 3 is longer than arguments 1-2"
):
list(zip_strict([0, 1], [2, 3], [1, 2, 3]))
|