2015-08-01 12:35:22 +02:00
|
|
|
from __future__ import print_function, division, absolute_import
|
|
|
|
from __future__ import unicode_literals
|
|
|
|
from fontTools.feaLib.lexer import LexerError
|
|
|
|
from fontTools.feaLib.parser import Parser, ParserError
|
|
|
|
from fontTools.misc.py23 import *
|
|
|
|
import codecs
|
|
|
|
import os
|
|
|
|
import shutil
|
|
|
|
import sys
|
|
|
|
import tempfile
|
|
|
|
import unittest
|
|
|
|
|
|
|
|
|
|
|
|
class ParserTest(unittest.TestCase):
|
2015-08-01 14:33:06 +02:00
|
|
|
def __init__(self, methodName):
|
|
|
|
unittest.TestCase.__init__(self, methodName)
|
|
|
|
# Python 3 renamed assertRaisesRegexp to assertRaisesRegex,
|
|
|
|
# and fires deprecation warnings if a program uses the old name.
|
|
|
|
if not hasattr(self, "assertRaisesRegex"):
|
|
|
|
self.assertRaisesRegex = self.assertRaisesRegexp
|
|
|
|
|
2015-08-01 17:34:02 +02:00
|
|
|
def test_glyphclass(self):
|
|
|
|
[gc] = self.parse("@dash = [endash emdash figuredash];").statements
|
|
|
|
self.assertEqual(gc.name, "dash")
|
|
|
|
self.assertEqual(gc.glyphs, {"endash", "emdash", "figuredash"})
|
|
|
|
|
2015-08-01 19:25:48 +02:00
|
|
|
def test_glyphclass_bad(self):
|
|
|
|
self.assertRaisesRegex(
|
|
|
|
ParserError, "Expected glyph name, range, or reference",
|
|
|
|
self.parse, "@bad = [a 123];")
|
|
|
|
|
2015-08-01 19:01:24 +02:00
|
|
|
def test_glyphclass_duplicate(self):
|
|
|
|
self.assertRaisesRegex(
|
|
|
|
ParserError, "Glyph class @dup already defined",
|
|
|
|
self.parse, "@dup = [a b]; @dup = [x];")
|
|
|
|
|
2015-08-01 19:25:48 +02:00
|
|
|
def test_glyphclass_empty(self):
|
|
|
|
[gc] = self.parse("@empty_set = [];").statements
|
|
|
|
self.assertEqual(gc.name, "empty_set")
|
|
|
|
self.assertEqual(gc.glyphs, set())
|
|
|
|
|
|
|
|
def test_glyphclass_equality(self):
|
|
|
|
[foo, bar] = self.parse("@foo = [a b]; @bar = @foo;").statements
|
|
|
|
self.assertEqual(foo.glyphs, {"a", "b"})
|
|
|
|
self.assertEqual(bar.glyphs, {"a", "b"})
|
|
|
|
|
2015-08-01 17:34:02 +02:00
|
|
|
def test_glyphclass_range_uppercase(self):
|
|
|
|
[gc] = self.parse("@swashes = [X.swash-Z.swash];").statements
|
|
|
|
self.assertEqual(gc.name, "swashes")
|
|
|
|
self.assertEqual(gc.glyphs, {"X.swash", "Y.swash", "Z.swash"})
|
|
|
|
|
|
|
|
def test_glyphclass_range_lowercase(self):
|
|
|
|
[gc] = self.parse("@defg.sc = [d.sc-g.sc];").statements
|
|
|
|
self.assertEqual(gc.name, "defg.sc")
|
|
|
|
self.assertEqual(gc.glyphs, {"d.sc", "e.sc", "f.sc", "g.sc"})
|
|
|
|
|
|
|
|
def test_glyphclass_range_digit1(self):
|
|
|
|
[gc] = self.parse("@range = [foo.2-foo.5];").statements
|
|
|
|
self.assertEqual(gc.glyphs, {"foo.2", "foo.3", "foo.4", "foo.5"})
|
|
|
|
|
|
|
|
def test_glyphclass_range_digit2(self):
|
|
|
|
[gc] = self.parse("@range = [foo.09-foo.11];").statements
|
|
|
|
self.assertEqual(gc.glyphs, {"foo.09", "foo.10", "foo.11"})
|
|
|
|
|
|
|
|
def test_glyphclass_range_digit3(self):
|
|
|
|
[gc] = self.parse("@range = [foo.123-foo.125];").statements
|
|
|
|
self.assertEqual(gc.glyphs, {"foo.123", "foo.124", "foo.125"})
|
|
|
|
|
|
|
|
def test_glyphclass_range_bad(self):
|
|
|
|
self.assertRaisesRegex(
|
|
|
|
ParserError,
|
|
|
|
"Bad range: \"a\" and \"foobar\" should have the same length",
|
|
|
|
self.parse, "@bad = [a-foobar];")
|
|
|
|
self.assertRaisesRegex(
|
|
|
|
ParserError, "Bad range: \"A.swash-z.swash\"",
|
|
|
|
self.parse, "@bad = [A.swash-z.swash];")
|
|
|
|
self.assertRaisesRegex(
|
|
|
|
ParserError, "Start of range must be smaller than its end",
|
|
|
|
self.parse, "@bad = [B.swash-A.swash];")
|
|
|
|
self.assertRaisesRegex(
|
|
|
|
ParserError, "Bad range: \"foo.1234-foo.9876\"",
|
|
|
|
self.parse, "@bad = [foo.1234-foo.9876];")
|
|
|
|
|
|
|
|
def test_glyphclass_range_mixed(self):
|
|
|
|
[gc] = self.parse("@range = [a foo.09-foo.11 X.sc-Z.sc];").statements
|
|
|
|
self.assertEqual(gc.glyphs, {
|
|
|
|
"a", "foo.09", "foo.10", "foo.11", "X.sc", "Y.sc", "Z.sc"
|
|
|
|
})
|
|
|
|
|
2015-08-01 19:25:48 +02:00
|
|
|
def test_glyphclass_reference(self):
|
|
|
|
[vowels_lc, vowels_uc, vowels] = self.parse(
|
|
|
|
"@Vowels.lc = [a e i o u]; @Vowels.uc = [A E I O U]; " +
|
|
|
|
"@Vowels = [@Vowels.lc @Vowels.uc y Y];").statements
|
|
|
|
self.assertEqual(vowels_lc.glyphs, set(list("aeiou")))
|
|
|
|
self.assertEqual(vowels_uc.glyphs, set(list("AEIOU")))
|
|
|
|
self.assertEqual(vowels.glyphs, set(list("aeiouyAEIOUY")))
|
|
|
|
self.assertRaisesRegex(
|
|
|
|
ParserError, "Unknown glyph class @unknown",
|
|
|
|
self.parse, "@bad = [@unknown];")
|
2015-08-01 17:34:02 +02:00
|
|
|
|
2015-08-01 12:35:22 +02:00
|
|
|
def test_languagesystem(self):
|
2015-08-01 14:49:19 +02:00
|
|
|
[langsys] = self.parse("languagesystem latn DEU;").statements
|
|
|
|
self.assertEqual(langsys.script, "latn")
|
|
|
|
self.assertEqual(langsys.language, "DEU ")
|
2015-08-01 14:33:06 +02:00
|
|
|
self.assertRaisesRegex(
|
2015-08-01 12:35:22 +02:00
|
|
|
ParserError, "Expected ';'",
|
|
|
|
self.parse, "languagesystem latn DEU")
|
2015-08-01 14:33:06 +02:00
|
|
|
self.assertRaisesRegex(
|
2015-08-01 12:35:22 +02:00
|
|
|
ParserError, "longer than 4 characters",
|
|
|
|
self.parse, "languagesystem foobar DEU")
|
2015-08-01 14:33:06 +02:00
|
|
|
self.assertRaisesRegex(
|
2015-08-01 12:35:22 +02:00
|
|
|
ParserError, "longer than 4 characters",
|
|
|
|
self.parse, "languagesystem latn FOOBAR")
|
|
|
|
|
|
|
|
def test_roundtrip(self):
|
|
|
|
self.roundtrip("mini.fea")
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
self.tempdir = None
|
|
|
|
self.num_tempfiles = 0
|
|
|
|
|
|
|
|
def tearDown(self):
|
|
|
|
if self.tempdir:
|
|
|
|
shutil.rmtree(self.tempdir)
|
|
|
|
|
|
|
|
def parse(self, text):
|
|
|
|
if not self.tempdir:
|
|
|
|
self.tempdir = tempfile.mkdtemp()
|
|
|
|
self.num_tempfiles += 1
|
|
|
|
path = os.path.join(self.tempdir, "tmp%d.fea" % self.num_tempfiles)
|
|
|
|
with codecs.open(path, "wb", "utf-8") as outfile:
|
|
|
|
outfile.write(text)
|
|
|
|
return Parser(path).parse()
|
|
|
|
|
|
|
|
def roundtrip(self, testfile):
|
|
|
|
buffer1, buffer2 = StringIO(), StringIO()
|
2015-08-01 14:49:19 +02:00
|
|
|
Parser(ParserTest.getpath(testfile)).parse().write(buffer1, os.linesep)
|
2015-08-01 12:35:22 +02:00
|
|
|
text1 = buffer1.getvalue().decode("utf-8")
|
2015-08-01 14:49:19 +02:00
|
|
|
self.parse(text1).write(buffer2, os.linesep)
|
2015-08-01 12:35:22 +02:00
|
|
|
text2 = buffer2.getvalue().decode("utf-8")
|
|
|
|
self.assertEqual(text1, text2)
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def getpath(testfile):
|
|
|
|
path, _ = os.path.split(__file__)
|
|
|
|
return os.path.join(path, "testdata", testfile)
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
unittest.main()
|