[otTables] Use XML snippets for testing fromXML() methods

This should make the unit tests more readable, and it also prevents
failures where a changed fromXML() implementation fails to ignore
interspersed whitespace. This has recently caused some developer
nuisance; see https://github.com/behdad/fonttools/pull/367.

Instead of having a custom parser implementation, it would be nicer
to call the actual XMLReader. However, XMLReader has a deeply built-in
assumption that it is processing an entire TTX file. Changing this
assumption would certainly be possible, but it would need a re-write
of the XMLReader code; having a custom parser just for unit tests
seems less involved.
This commit is contained in:
Sascha Brawer 2015-09-11 15:34:01 +02:00
parent 99acc50215
commit d63885e337
3 changed files with 112 additions and 31 deletions

View File

@ -0,0 +1,42 @@
"""Helpers for writing unit tests."""
from __future__ import print_function, division, absolute_import
from fontTools.misc.py23 import *
def parseXML(xmlSnippet):
"""Parses a snippet of XML.
The result is in the same format that would be returned by
XMLReader, but the parser imposes no constraints on the root
element so it can be called on small snippets of TTX files.
"""
# To support snippets with multiple elements, we add a fake root.
reader = TestXMLReader_()
reader.parser.Parse("<root>%s</root>" % xmlSnippet, 0)
return reader.root[2]
class TestXMLReader_(object):
def __init__(self):
from xml.parsers.expat import ParserCreate
self.parser = ParserCreate()
self.parser.StartElementHandler = self.startElement_
self.parser.EndElementHandler = self.endElement_
self.parser.CharacterDataHandler = self.addCharacterData_
self.root = None
self.stack = []
def startElement_(self, name, attrs):
element = (name, attrs, [])
if self.stack:
self.stack[-1][2].append(element)
else:
self.root = element
self.stack.append(element)
def endElement_(self, name):
self.stack.pop()
def addCharacterData_(self, data):
self.stack[-1][2].append(data)

View File

@ -0,0 +1,30 @@
from __future__ import print_function, division, absolute_import
from __future__ import unicode_literals
from fontTools.misc.py23 import *
import fontTools.misc.testTools as testTools
import os
import unittest
class TestToolsTest(unittest.TestCase):
def test_parseXML(self):
self.assertEqual(testTools.parseXML(
'<Foo n="1"/>'
'<Foo n="2">'
' some text'
' <Bar color="red"/>'
' some more text'
'</Foo>'
'<Foo n="3"/>'), [
("Foo", {"n": "1"}, []),
("Foo", {"n": "2"}, [
" some text ",
("Bar", {"color": "red"}, []),
" some more text",
]),
("Foo", {"n": "3"}, [])
])
if __name__ == "__main__":
unittest.main()

View File

@ -1,5 +1,6 @@
from __future__ import print_function, division, absolute_import from __future__ import print_function, division, absolute_import
from fontTools.misc.py23 import * from fontTools.misc.py23 import *
from fontTools.misc.testTools import parseXML
from fontTools.misc.xmlWriter import XMLWriter from fontTools.misc.xmlWriter import XMLWriter
import fontTools.ttLib.tables.otTables as otTables import fontTools.ttLib.tables.otTables as otTables
import unittest import unittest
@ -83,9 +84,11 @@ class SingleSubstTest(unittest.TestCase):
def test_fromXML(self): def test_fromXML(self):
table = otTables.SingleSubst() table = otTables.SingleSubst()
table.fromXML("Substitution", {"in": "A", "out": "a"}, [], self.font) for name, attrs, content in parseXML(
table.fromXML("Substitution", {"in": "B", "out": "b"}, [], self.font) '<Substitution in="A" out="a"/>'
table.fromXML("Substitution", {"in": "C", "out": "c"}, [], self.font) '<Substitution in="B" out="b"/>'
'<Substitution in="C" out="c"/>'):
table.fromXML(name, attrs, content, self.font)
self.assertEqual(table.mapping, {"A": "a", "B": "b", "C": "c"}) self.assertEqual(table.mapping, {"A": "a", "B": "b", "C": "c"})
@ -135,28 +138,30 @@ class MultipleSubstTest(unittest.TestCase):
def test_fromXML(self): def test_fromXML(self):
table = otTables.MultipleSubst() table = otTables.MultipleSubst()
table.fromXML("Substitution", for name, attrs, content in parseXML(
{"in": "c_t", "out": "c,t"}, [], self.font) '<Substitution in="c_t" out="c,t"/>'
table.fromXML("Substitution", '<Substitution in="f_f_i" out="f,f,i"/>'):
{"in": "f_f_i", "out": "f,f,i"}, [], self.font) table.fromXML(name, attrs, content, self.font)
self.assertEqual(table.mapping, self.assertEqual(table.mapping,
{'c_t': ['c', 't'], 'f_f_i': ['f', 'f', 'i']}) {'c_t': ['c', 't'], 'f_f_i': ['f', 'f', 'i']})
def test_fromXML_oldFormat(self): def test_fromXML_oldFormat(self):
table = otTables.MultipleSubst() table = otTables.MultipleSubst()
table.fromXML("Coverage", {}, [ for name, attrs, content in parseXML(
("Glyph", {"value": "c_t"}, []), '<Coverage>'
("Glyph", {"value": "f_f_i"}, []) ' <Glyph value="c_t"/>'
], self.font) ' <Glyph value="f_f_i"/>'
table.fromXML("Sequence", {"index": "0"}, [ '</Coverage>'
("Substitute", {"index": "0", "value": "c"}, []), '<Sequence index="0">'
("Substitute", {"index": "1", "value": "t"}, []) ' <Substitute index="0" value="c"/>'
], self.font) ' <Substitute index="1" value="t"/>'
table.fromXML("Sequence", {"index": "1"}, [ '</Sequence>'
("Substitute", {"index": "0", "value": "f"}, []), '<Sequence index="1">'
("Substitute", {"index": "1", "value": "f"}, []), ' <Substitute index="0" value="f"/>'
("Substitute", {"index": "2", "value": "i"}, []) ' <Substitute index="1" value="f"/>'
], self.font) ' <Substitute index="2" value="i"/>'
'</Sequence>'):
table.fromXML(name, attrs, content, self.font)
self.assertEqual(table.mapping, self.assertEqual(table.mapping,
{'c_t': ['c', 't'], 'f_f_i': ['f', 'f', 'i']}) {'c_t': ['c', 't'], 'f_f_i': ['f', 'f', 'i']})
@ -255,10 +260,12 @@ class LigatureSubstTest(unittest.TestCase):
def test_fromXML(self): def test_fromXML(self):
table = otTables.LigatureSubst() table = otTables.LigatureSubst()
table.fromXML("LigatureSet", {"glyph": "f"}, [ for name, attrs, content in parseXML(
("Ligature", {"components": "f,f,i", "glyph": "f_f_i"}, []), '<LigatureSet glyph="f">'
("Ligature", {"components": "f,f", "glyph": "f_f"}, []), ' <Ligature components="f,f,i" glyph="f_f_i"/>'
], self.font) ' <Ligature components="f,f" glyph="f_f"/>'
'</LigatureSet>'):
table.fromXML(name, attrs, content, self.font)
self.assertEqual(set(table.ligatures.keys()), {"f"}) self.assertEqual(set(table.ligatures.keys()), {"f"})
[ffi, ff] = table.ligatures["f"] [ffi, ff] = table.ligatures["f"]
self.assertEqual(ffi.LigGlyph, "f_f_i") self.assertEqual(ffi.LigGlyph, "f_f_i")
@ -327,13 +334,15 @@ class AlternateSubstTest(unittest.TestCase):
def test_fromXML(self): def test_fromXML(self):
table = otTables.AlternateSubst() table = otTables.AlternateSubst()
table.fromXML("AlternateSet", {"glyph": "G"}, [ for name, attrs, content in parseXML(
("Alternate", {"glyph": "G.alt1"}, []), '<AlternateSet glyph="G">'
("Alternate", {"glyph": "G.alt2"}, []) ' <Alternate glyph="G.alt1"/>'
], self.font) ' <Alternate glyph="G.alt2"/>'
table.fromXML("AlternateSet", {"glyph": "Z"}, [ '</AlternateSet>'
("Alternate", {"glyph": "Z.fina"}, []) '<AlternateSet glyph="Z">'
], self.font) ' <Alternate glyph="Z.fina"/>'
'</AlternateSet>'):
table.fromXML(name, attrs, content, self.font)
self.assertEqual(table.alternates, { self.assertEqual(table.alternates, {
"G": ["G.alt1", "G.alt2"], "G": ["G.alt1", "G.alt2"],
"Z": ["Z.fina"] "Z": ["Z.fina"]