From d63885e33700807dfd5b66072ec6195f666f7572 Mon Sep 17 00:00:00 2001 From: Sascha Brawer Date: Fri, 11 Sep 2015 15:34:01 +0200 Subject: [PATCH] [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. --- Lib/fontTools/misc/testTools.py | 42 ++++++++++++ Lib/fontTools/misc/testTools_test.py | 30 +++++++++ Lib/fontTools/ttLib/tables/otTables_test.py | 71 ++++++++++++--------- 3 files changed, 112 insertions(+), 31 deletions(-) create mode 100644 Lib/fontTools/misc/testTools.py create mode 100644 Lib/fontTools/misc/testTools_test.py diff --git a/Lib/fontTools/misc/testTools.py b/Lib/fontTools/misc/testTools.py new file mode 100644 index 000000000..b751f946b --- /dev/null +++ b/Lib/fontTools/misc/testTools.py @@ -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("%s" % 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) diff --git a/Lib/fontTools/misc/testTools_test.py b/Lib/fontTools/misc/testTools_test.py new file mode 100644 index 000000000..3500f8a50 --- /dev/null +++ b/Lib/fontTools/misc/testTools_test.py @@ -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( + '' + '' + ' some text' + ' ' + ' some more text' + '' + ''), [ + ("Foo", {"n": "1"}, []), + ("Foo", {"n": "2"}, [ + " some text ", + ("Bar", {"color": "red"}, []), + " some more text", + ]), + ("Foo", {"n": "3"}, []) + ]) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/fontTools/ttLib/tables/otTables_test.py b/Lib/fontTools/ttLib/tables/otTables_test.py index 24a7fa362..8a3be8e6a 100644 --- a/Lib/fontTools/ttLib/tables/otTables_test.py +++ b/Lib/fontTools/ttLib/tables/otTables_test.py @@ -1,5 +1,6 @@ from __future__ import print_function, division, absolute_import from fontTools.misc.py23 import * +from fontTools.misc.testTools import parseXML from fontTools.misc.xmlWriter import XMLWriter import fontTools.ttLib.tables.otTables as otTables import unittest @@ -83,9 +84,11 @@ class SingleSubstTest(unittest.TestCase): def test_fromXML(self): table = otTables.SingleSubst() - table.fromXML("Substitution", {"in": "A", "out": "a"}, [], self.font) - table.fromXML("Substitution", {"in": "B", "out": "b"}, [], self.font) - table.fromXML("Substitution", {"in": "C", "out": "c"}, [], self.font) + for name, attrs, content in parseXML( + '' + '' + ''): + table.fromXML(name, attrs, content, self.font) self.assertEqual(table.mapping, {"A": "a", "B": "b", "C": "c"}) @@ -135,28 +138,30 @@ class MultipleSubstTest(unittest.TestCase): def test_fromXML(self): table = otTables.MultipleSubst() - table.fromXML("Substitution", - {"in": "c_t", "out": "c,t"}, [], self.font) - table.fromXML("Substitution", - {"in": "f_f_i", "out": "f,f,i"}, [], self.font) + for name, attrs, content in parseXML( + '' + ''): + table.fromXML(name, attrs, content, self.font) self.assertEqual(table.mapping, {'c_t': ['c', 't'], 'f_f_i': ['f', 'f', 'i']}) def test_fromXML_oldFormat(self): table = otTables.MultipleSubst() - table.fromXML("Coverage", {}, [ - ("Glyph", {"value": "c_t"}, []), - ("Glyph", {"value": "f_f_i"}, []) - ], self.font) - table.fromXML("Sequence", {"index": "0"}, [ - ("Substitute", {"index": "0", "value": "c"}, []), - ("Substitute", {"index": "1", "value": "t"}, []) - ], self.font) - table.fromXML("Sequence", {"index": "1"}, [ - ("Substitute", {"index": "0", "value": "f"}, []), - ("Substitute", {"index": "1", "value": "f"}, []), - ("Substitute", {"index": "2", "value": "i"}, []) - ], self.font) + for name, attrs, content in parseXML( + '' + ' ' + ' ' + '' + '' + ' ' + ' ' + '' + '' + ' ' + ' ' + ' ' + ''): + table.fromXML(name, attrs, content, self.font) self.assertEqual(table.mapping, {'c_t': ['c', 't'], 'f_f_i': ['f', 'f', 'i']}) @@ -255,10 +260,12 @@ class LigatureSubstTest(unittest.TestCase): def test_fromXML(self): table = otTables.LigatureSubst() - table.fromXML("LigatureSet", {"glyph": "f"}, [ - ("Ligature", {"components": "f,f,i", "glyph": "f_f_i"}, []), - ("Ligature", {"components": "f,f", "glyph": "f_f"}, []), - ], self.font) + for name, attrs, content in parseXML( + '' + ' ' + ' ' + ''): + table.fromXML(name, attrs, content, self.font) self.assertEqual(set(table.ligatures.keys()), {"f"}) [ffi, ff] = table.ligatures["f"] self.assertEqual(ffi.LigGlyph, "f_f_i") @@ -327,13 +334,15 @@ class AlternateSubstTest(unittest.TestCase): def test_fromXML(self): table = otTables.AlternateSubst() - table.fromXML("AlternateSet", {"glyph": "G"}, [ - ("Alternate", {"glyph": "G.alt1"}, []), - ("Alternate", {"glyph": "G.alt2"}, []) - ], self.font) - table.fromXML("AlternateSet", {"glyph": "Z"}, [ - ("Alternate", {"glyph": "Z.fina"}, []) - ], self.font) + for name, attrs, content in parseXML( + '' + ' ' + ' ' + '' + '' + ' ' + ''): + table.fromXML(name, attrs, content, self.font) self.assertEqual(table.alternates, { "G": ["G.alt1", "G.alt2"], "Z": ["Z.fina"]