File: util_test.py

package info (click to toggle)
ufo2ft 3.7.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 6,528 kB
  • sloc: python: 18,685; makefile: 10
file content (110 lines) | stat: -rw-r--r-- 4,155 bytes parent folder | download | duplicates (2)
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]))