diff --git a/Tests/ttx/data/TestBOM.ttx b/Tests/ttx/data/TestBOM.ttx
new file mode 100644
index 000000000..7597133da
--- /dev/null
+++ b/Tests/ttx/data/TestBOM.ttx
@@ -0,0 +1,3 @@
+
+
+
diff --git a/Tests/ttx/data/TestDFONT.dfont b/Tests/ttx/data/TestDFONT.dfont
new file mode 100644
index 000000000..a6ea7009b
Binary files /dev/null and b/Tests/ttx/data/TestDFONT.dfont differ
diff --git a/Tests/ttx/data/TestFFIL b/Tests/ttx/data/TestFFIL
new file mode 100755
index 000000000..e69de29bb
diff --git a/Tests/ttx/data/TestNoSFNT.ttx b/Tests/ttx/data/TestNoSFNT.ttx
new file mode 100644
index 000000000..c71198d13
--- /dev/null
+++ b/Tests/ttx/data/TestNoSFNT.ttx
@@ -0,0 +1,2 @@
+
+
diff --git a/Tests/ttx/data/TestNoXML.ttx b/Tests/ttx/data/TestNoXML.ttx
new file mode 100644
index 000000000..8dac50745
--- /dev/null
+++ b/Tests/ttx/data/TestNoXML.ttx
@@ -0,0 +1,2 @@
+
+
diff --git a/Tests/ttx/data/TestOTF.otf b/Tests/ttx/data/TestOTF.otf
new file mode 100644
index 000000000..cd0bdf67d
Binary files /dev/null and b/Tests/ttx/data/TestOTF.otf differ
diff --git a/Tests/ttx/data/TestOTF.ttx b/Tests/ttx/data/TestOTF.ttx
new file mode 100644
index 000000000..852dacfa8
--- /dev/null
+++ b/Tests/ttx/data/TestOTF.ttx
@@ -0,0 +1,519 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Copyright (c) 2015 by FontTools. No rights reserved.
+
+
+ Test OTF
+
+
+ Regular
+
+
+ FontTools: Test OTF: 2015
+
+
+ Test OTF
+
+
+ Version 1.000
+
+
+ TestOTF-Regular
+
+
+ Test OTF is not a trademark of FontTools.
+
+
+ FontTools
+
+
+ FontTools
+
+
+ https://github.com/behdad/fonttools
+
+
+ https://github.com/behdad/fonttools
+
+
+ https://github.com/behdad/fonttools/blob/master/LICENSE.txt
+
+
+ Test TTF
+
+
+ Copyright (c) 2015 by FontTools. No rights reserved.
+
+
+ Test OTF
+
+
+ Regular
+
+
+ FontTools: Test OTF: 2015
+
+
+ Test OTF
+
+
+ Version 1.000
+
+
+ TestOTF-Regular
+
+
+ Test OTF is not a trademark of FontTools.
+
+
+ FontTools
+
+
+ FontTools
+
+
+ https://github.com/behdad/fonttools
+
+
+ https://github.com/behdad/fonttools
+
+
+ https://github.com/behdad/fonttools/blob/master/LICENSE.txt
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 131 122 -131 hlineto
+ return
+
+
+
+
+
+ 500 450 hmoveto
+ 750 -400 -750 vlineto
+ 50 50 rmoveto
+ 650 300 -650 vlineto
+ endchar
+
+
+ 0 endchar
+
+
+ 250 endchar
+
+
+ 723 55 hmoveto
+ -107 callsubr
+ 241 -122 rmoveto
+ -107 callsubr
+ 241 -122 rmoveto
+ -107 callsubr
+ endchar
+
+
+ 241 55 hmoveto
+ -107 callsubr
+ endchar
+
+
+ 250 endchar
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Tests/ttx/data/TestTTC.ttc b/Tests/ttx/data/TestTTC.ttc
new file mode 100644
index 000000000..a21fe89dc
Binary files /dev/null and b/Tests/ttx/data/TestTTC.ttc differ
diff --git a/Tests/ttx/data/TestTTF.ttf b/Tests/ttx/data/TestTTF.ttf
new file mode 100644
index 000000000..e906d012d
Binary files /dev/null and b/Tests/ttx/data/TestTTF.ttf differ
diff --git a/Tests/ttx/data/TestTTF.ttx b/Tests/ttx/data/TestTTF.ttx
new file mode 100644
index 000000000..c283a295f
--- /dev/null
+++ b/Tests/ttx/data/TestTTF.ttx
@@ -0,0 +1,553 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ SVTCA[0] /* SetFPVectorToAxis */
+
+
+
+
+
+ SVTCA[0] /* SetFPVectorToAxis */
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ SVTCA[0] /* SetFPVectorToAxis */
+ SVTCA[1] /* SetFPVectorToAxis */
+
+
+
+
+
+
+
+
+
+
+
+
+ SVTCA[0] /* SetFPVectorToAxis */
+ SVTCA[1] /* SetFPVectorToAxis */
+
+
+
+
+
+
+
+
+
+
+
+ SVTCA[0] /* SetFPVectorToAxis */
+ SVTCA[1] /* SetFPVectorToAxis */
+
+
+
+
+
+
+
+
+
+ Copyright (c) 2015 by FontTools. No rights reserved.
+
+
+ Test TTF
+
+
+ Regular
+
+
+ FontTools: Test TTF: 2015
+
+
+ Test TTF
+
+
+ Version 1.000
+
+
+ TestTTF-Regular
+
+
+ Test TTF is not a trademark of FontTools.
+
+
+ FontTools
+
+
+ FontTools
+
+
+ https://github.com/behdad/fonttools
+
+
+ https://github.com/behdad/fonttools
+
+
+ https://github.com/behdad/fonttools/blob/master/LICENSE.txt
+
+
+ Test TTF
+
+
+ Copyright (c) 2015 by FontTools. No rights reserved.
+
+
+ Test TTF
+
+
+ Regular
+
+
+ FontTools: Test TTF: 2015
+
+
+ Test TTF
+
+
+ Version 1.000
+
+
+ TestTTF-Regular
+
+
+ Test TTF is not a trademark of FontTools.
+
+
+ FontTools
+
+
+ FontTools
+
+
+ https://github.com/behdad/fonttools
+
+
+ https://github.com/behdad/fonttools
+
+
+ https://github.com/behdad/fonttools/blob/master/LICENSE.txt
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Tests/ttx/data/TestWOFF.woff b/Tests/ttx/data/TestWOFF.woff
new file mode 100644
index 000000000..64cd78dc8
Binary files /dev/null and b/Tests/ttx/data/TestWOFF.woff differ
diff --git a/Tests/ttx/data/TestWOFF2.woff2 b/Tests/ttx/data/TestWOFF2.woff2
new file mode 100644
index 000000000..a345573f7
Binary files /dev/null and b/Tests/ttx/data/TestWOFF2.woff2 differ
diff --git a/Tests/ttx/ttx_test.py b/Tests/ttx/ttx_test.py
new file mode 100644
index 000000000..97287d7ff
--- /dev/null
+++ b/Tests/ttx/ttx_test.py
@@ -0,0 +1,202 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools import ttx
+import getopt
+import os
+import shutil
+import sys
+import tempfile
+import unittest
+
+try:
+ import xattr
+except ImportError:
+ xattr = None
+try:
+ import MacOS
+except ImportError:
+ MacOS = None
+
+
+class TTXTest(unittest.TestCase):
+ 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
+
+ def setUp(self):
+ self.tempdir = None
+ self.num_tempfiles = 0
+
+ def tearDown(self):
+ if self.tempdir:
+ shutil.rmtree(self.tempdir)
+
+ @staticmethod
+ def getpath(testfile):
+ path, _ = os.path.split(__file__)
+ return os.path.join(path, "data", testfile)
+
+ def temp_dir(self):
+ if not self.tempdir:
+ self.tempdir = tempfile.mkdtemp()
+
+ def temp_font(self, font_path, file_name):
+ self.temp_dir()
+ temppath = os.path.join(self.tempdir, file_name)
+ shutil.copy2(font_path, temppath)
+ return temppath
+
+# -----
+# Tests
+# -----
+
+ def test_parseOptions_no_args(self):
+ with self.assertRaises(getopt.GetoptError) as cm:
+ ttx.parseOptions([])
+ self.assertTrue('Must specify at least one input file' in str(cm.exception))
+
+ def test_parseOptions_invalid_path(self):
+ file_path = 'invalid_font_path'
+ with self.assertRaises(getopt.GetoptError) as cm:
+ ttx.parseOptions([file_path])
+ self.assertTrue('File not found: "%s"' % file_path in str(cm.exception))
+
+ def test_parseOptions_font2ttx_1st_time(self):
+ file_name = 'TestOTF.otf'
+ font_path = self.getpath(file_name)
+ temp_path = self.temp_font(font_path, file_name)
+ jobs, _ = ttx.parseOptions([temp_path])
+ self.assertEqual(jobs[0][0].__name__, 'ttDump')
+ self.assertEqual(jobs[0][1:],
+ (os.path.join(self.tempdir, file_name),
+ os.path.join(self.tempdir, file_name.split('.')[0] + '.ttx')))
+
+ def test_parseOptions_font2ttx_2nd_time(self):
+ file_name = 'TestTTF.ttf'
+ font_path = self.getpath(file_name)
+ temp_path = self.temp_font(font_path, file_name)
+ _, _ = ttx.parseOptions([temp_path]) # this is NOT a mistake
+ jobs, _ = ttx.parseOptions([temp_path])
+ self.assertEqual(jobs[0][0].__name__, 'ttDump')
+ self.assertEqual(jobs[0][1:],
+ (os.path.join(self.tempdir, file_name),
+ os.path.join(self.tempdir, file_name.split('.')[0] + '#1.ttx')))
+
+ def test_parseOptions_ttx2font_1st_time(self):
+ file_name = 'TestTTF.ttx'
+ font_path = self.getpath(file_name)
+ temp_path = self.temp_font(font_path, file_name)
+ jobs, _ = ttx.parseOptions([temp_path])
+ self.assertEqual(jobs[0][0].__name__, 'ttCompile')
+ self.assertEqual(jobs[0][1:],
+ (os.path.join(self.tempdir, file_name),
+ os.path.join(self.tempdir, file_name.split('.')[0] + '.ttf')))
+
+ def test_parseOptions_ttx2font_2nd_time(self):
+ file_name = 'TestOTF.ttx'
+ font_path = self.getpath(file_name)
+ temp_path = self.temp_font(font_path, file_name)
+ _, _ = ttx.parseOptions([temp_path]) # this is NOT a mistake
+ jobs, _ = ttx.parseOptions([temp_path])
+ self.assertEqual(jobs[0][0].__name__, 'ttCompile')
+ self.assertEqual(jobs[0][1:],
+ (os.path.join(self.tempdir, file_name),
+ os.path.join(self.tempdir, file_name.split('.')[0] + '#1.otf')))
+
+ def test_parseOptions_multiple_fonts(self):
+ file_names = ['TestOTF.otf', 'TestTTF.ttf']
+ font_paths = [self.getpath(file_name) for file_name in file_names]
+ temp_paths = [self.temp_font(font_path, file_name) \
+ for font_path, file_name in zip(font_paths, file_names)]
+ jobs, _ = ttx.parseOptions(temp_paths)
+ for i in range(len(jobs)):
+ self.assertEqual(jobs[i][0].__name__, 'ttDump')
+ self.assertEqual(jobs[i][1:],
+ (os.path.join(self.tempdir, file_names[i]),
+ os.path.join(self.tempdir, file_names[i].split('.')[0] + '.ttx')))
+
+ def test_parseOptions_mixed_files(self):
+ operations = ['ttDump', 'ttCompile']
+ extensions = ['.ttx', '.ttf']
+ file_names = ['TestOTF.otf', 'TestTTF.ttx']
+ font_paths = [self.getpath(file_name) for file_name in file_names]
+ temp_paths = [self.temp_font(font_path, file_name) \
+ for font_path, file_name in zip(font_paths, file_names)]
+ jobs, _ = ttx.parseOptions(temp_paths)
+ for i in range(len(jobs)):
+ self.assertEqual(jobs[i][0].__name__, operations[i])
+ self.assertEqual(jobs[i][1:],
+ (os.path.join(self.tempdir, file_names[i]),
+ os.path.join(self.tempdir, file_names[i].split('.')[0] + extensions[i])))
+
+ def test_guessFileType_ttf(self):
+ file_name = 'TestTTF.ttf'
+ font_path = self.getpath(file_name)
+ self.assertEqual(ttx.guessFileType(font_path), 'TTF')
+
+ def test_guessFileType_otf(self):
+ file_name = 'TestOTF.otf'
+ font_path = self.getpath(file_name)
+ self.assertEqual(ttx.guessFileType(font_path), 'OTF')
+
+ def test_guessFileType_woff(self):
+ file_name = 'TestWOFF.woff'
+ font_path = self.getpath(file_name)
+ self.assertEqual(ttx.guessFileType(font_path), 'WOFF')
+
+ def test_guessFileType_woff2(self):
+ file_name = 'TestWOFF2.woff2'
+ font_path = self.getpath(file_name)
+ self.assertEqual(ttx.guessFileType(font_path), 'WOFF2')
+
+ def test_guessFileType_ttc(self):
+ file_name = 'TestTTC.ttc'
+ font_path = self.getpath(file_name)
+ self.assertEqual(ttx.guessFileType(font_path), 'TTC')
+
+ def test_guessFileType_dfont(self):
+ file_name = 'TestDFONT.dfont'
+ font_path = self.getpath(file_name)
+ self.assertEqual(ttx.guessFileType(font_path), 'TTF')
+
+ @unittest.skipUnless(xattr or MacOS, "xattr or MacOS not installed")
+ def test_guessFileType_ffil(self):
+ file_name = 'TestFFIL'
+ font_path = self.getpath(file_name)
+ self.assertEqual(ttx.guessFileType(font_path), 'TTF')
+
+ def test_guessFileType_ttx_ttf(self):
+ file_name = 'TestTTF.ttx'
+ font_path = self.getpath(file_name)
+ self.assertEqual(ttx.guessFileType(font_path), 'TTX')
+
+ def test_guessFileType_ttx_otf(self):
+ file_name = 'TestOTF.ttx'
+ font_path = self.getpath(file_name)
+ self.assertEqual(ttx.guessFileType(font_path), 'OTX')
+
+ def test_guessFileType_ttx_bom(self):
+ file_name = 'TestBOM.ttx'
+ font_path = self.getpath(file_name)
+ self.assertEqual(ttx.guessFileType(font_path), 'TTX')
+
+ def test_guessFileType_ttx_no_sfntVersion(self):
+ file_name = 'TestNoSFNT.ttx'
+ font_path = self.getpath(file_name)
+ self.assertEqual(ttx.guessFileType(font_path), 'TTX')
+
+ def test_guessFileType_ttx_no_xml(self):
+ file_name = 'TestNoXML.ttx'
+ font_path = self.getpath(file_name)
+ self.assertIsNone(ttx.guessFileType(font_path))
+
+ def test_guessFileType_invalid_path(self):
+ font_path = 'invalid_font_path'
+ self.assertIsNone(ttx.guessFileType(font_path))
+
+
+if __name__ == "__main__":
+ sys.exit(unittest.main())