Adds ttx test harness

This commit is contained in:
Yury Delendik 2012-11-01 18:10:47 -05:00
parent 9d3afdd2e0
commit 5afec33956
6 changed files with 304 additions and 16 deletions

26
make.js
View File

@ -664,7 +664,9 @@ target.test = function() {
//
target.bottest = function() {
target.unittest({}, function() {
target.browsertest({noreftest: true});
target.fonttest({}, function() {
target.browsertest({noreftest: true});
});
});
};
@ -715,6 +717,28 @@ target.unittest = function(options, callback) {
PDF_BROWSERS, {async: true}, callback);
};
//
// make fonttest
//
target.fonttest = function(options, callback) {
cd(ROOT_DIR);
echo();
echo('### Running font tests');
var PDF_BROWSERS = env['PDF_BROWSERS'] ||
'resources/browser_manifests/browser_manifest.json';
if (!test('-f', 'test/' + PDF_BROWSERS)) {
echo('Browser manifest file test/' + PDF_BROWSERS + ' does not exist.');
echo('Copy one of the examples in test/resources/browser_manifests/');
exit(1);
}
callback = callback || function() {};
cd('test');
exec(PYTHON_BIN + ' -u test.py --fontTest --browserManifestFile=' +
PDF_BROWSERS, {async: true}, callback);
};
//
// make botmakeref
//

View File

@ -0,0 +1,17 @@
'use strict';
describe('font1', function() {
var font1_1 = decodeFontData('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');
describe('test harness testing', function() {
it('returns output', function() {
var output;
waitsFor(function() { return output; }, 10000);
ttx(font1_1, function(result) { output = result; });
runs(function() {
verifyTtxOutput(output);
expect(/<ttFont /.test(output)).toEqual(true);
expect(/<\/ttFont>/.test(output)).toEqual(true);
});
});
});
});

91
test/font/font_test.html Normal file
View File

@ -0,0 +1,91 @@
<!DOCTYPE html>
<html>
<head>
<title>pdf.js unit test</title>
<link rel="shortcut icon" type="image/png" href="../../external/jasmine/jasmine_favicon.png">
<link rel="stylesheet" type="text/css" href="../../external/jasmine/jasmine.css">
<script type="text/javascript" src="../../external/jasmine/jasmine.js"></script>
<script type="text/javascript" src="../../external/jasmine/jasmine-html.js"></script>
<script type="text/javascript" src="../unit/testreporter.js"></script>
<script type="text/javascript" src="fontutils.js"></script>
<!-- include source files here... -->
<script type="text/javascript" src="../../src/core.js"></script>
<script type="text/javascript" src="../../src/api.js"></script>
<script type="text/javascript" src="../../src/util.js"></script>
<script type="text/javascript" src="../../src/canvas.js"></script>
<script type="text/javascript" src="../../src/obj.js"></script>
<script type="text/javascript" src="../../src/function.js"></script>
<script type="text/javascript" src="../../src/charsets.js"></script>
<script type="text/javascript" src="../../src/cidmaps.js"></script>
<script type="text/javascript" src="../../src/colorspace.js"></script>
<script type="text/javascript" src="../../src/crypto.js"></script>
<script type="text/javascript" src="../../src/evaluator.js"></script>
<script type="text/javascript" src="../../src/fonts.js"></script>
<script type="text/javascript" src="../../src/glyphlist.js"></script>
<script type="text/javascript" src="../../src/image.js"></script>
<script type="text/javascript" src="../../src/metrics.js"></script>
<script type="text/javascript" src="../../src/parser.js"></script>
<script type="text/javascript" src="../../src/pattern.js"></script>
<script type="text/javascript" src="../../src/stream.js"></script>
<script type="text/javascript" src="../../src/worker.js"></script>
<script type="text/javascript" src="../../src/metadata.js"></script>
<script type="text/javascript" src="../../external/jpgjs/jpg.js"></script>
<script type="text/javascript">PDFJS.workerSrc = '../../src/worker_loader.js';</script>
<!-- include spec files here... -->
<script type="text/javascript" src="font_core_spec.js"></script>
<script type="text/javascript">
'use strict';
(function pdfJsUnitTest() {
function queryParams() {
var qs = window.location.search.substring(1);
var kvs = qs.split('&');
var params = { };
for (var i = 0; i < kvs.length; ++i) {
var kv = kvs[i].split('=');
params[unescape(kv[0])] = unescape(kv[1]);
}
return params;
}
var jasmineEnv = jasmine.getEnv();
jasmineEnv.updateInterval = 1000;
var trivialReporter = new jasmine.TrivialReporter();
jasmineEnv.addReporter(trivialReporter);
var params = queryParams();
if (params['browser']) {
var testReporter = new TestReporter(params['browser'], params['path']);
jasmineEnv.addReporter(testReporter);
}
jasmineEnv.specFilter = function pdfJsUnitTestSpecFilter(spec) {
return trivialReporter.specFilter(spec);
};
var currentWindowOnload = window.onload;
window.onload = function pdfJsUnitTestOnload() {
if (currentWindowOnload) {
currentWindowOnload();
}
execJasmine();
};
function execJasmine() {
jasmineEnv.execute();
}
})();
</script>
</head>
<body>
</body>
</html>

67
test/font/fontutils.js Normal file
View File

@ -0,0 +1,67 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
var base64alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
function decodeFontData(base64) {
var result = [];
var bits = 0, bitsLength = 0;
for (var i = 0, ii = base64.length; i < ii; i++) {
var ch = base64[i];
if (ch <= " ") continue;
var index = base64alphabet.indexOf(ch);
if (index < 0) throw "Invalid character";
if (index >= 64) break;
bits = (bits << 6) | index;
bitsLength += 6;
if (bitsLength >= 8) {
bitsLength -= 8
var code = (bits >> bitsLength) & 0xFF;
result.push(code);
}
}
return new Uint8Array(result);
}
function encodeFontData(data) {
var buffer = '';
var i, n;
for (i = 0, n = data.length; i < n; i += 3) {
var b1 = data[i] & 0xFF;
var b2 = data[i + 1] & 0xFF;
var b3 = data[i + 2] & 0xFF;
var d1 = b1 >> 2, d2 = ((b1 & 3) << 4) | (b2 >> 4);
var d3 = i + 1 < n ? ((b2 & 0xF) << 2) | (b3 >> 6) : 64;
var d4 = i + 2 < n ? (b3 & 0x3F) : 64;
buffer += (base64alphabet.charAt(d1) + base64alphabet.charAt(d2) +
base64alphabet.charAt(d3) + base64alphabet.charAt(d4));
}
return buffer;
}
function ttx(data, callback) {
var xhr = new XMLHttpRequest();
xhr.open('POST', '/ttx');
var encodedData = encodeFontData(data);
xhr.setRequestHeader("Content-type", "text/plain");
xhr.setRequestHeader("Content-length", encodedData.length);
xhr.onreadystatechange = function getPdfOnreadystatechange(e) {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
callback(xhr.responseText);
} else {
callback('<error>Transport error: ' + xhr.statusText + '</error>');
}
}
};
xhr.send(encodedData);
}
function verifyTtxOutput(output) {
var m = /^<error>(.*?)<\/error>/.exec(output);
if (m)
throw m[1];
}

View File

@ -12,7 +12,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import json, platform, os, shutil, sys, subprocess, tempfile, threading, time, urllib, urllib2, hashlib
import json, platform, os, shutil, sys, subprocess, tempfile, threading
import time, urllib, urllib2, hashlib, re, base64, uuid, socket, errno
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
import SocketServer
from optparse import OptionParser
@ -55,6 +56,8 @@ class TestOptions(OptionParser):
help="The port the HTTP server should listen on.", default=8080)
self.add_option("--unitTest", action="store_true", dest="unitTest",
help="Run the unit tests.", default=False)
self.add_option("--fontTest", action="store_true", dest="fontTest",
help="Run the font tests.", default=False)
self.add_option("--noDownload", action="store_true", dest="noDownload",
help="Skips test PDFs downloading.", default=False)
self.add_option("--ignoreDownloadErrors", action="store_true", dest="ignoreDownloadErrors",
@ -62,8 +65,8 @@ class TestOptions(OptionParser):
self.set_usage(USAGE_EXAMPLE)
def verifyOptions(self, options):
if options.reftest and options.unitTest:
self.error("--reftest and --unitTest must not be specified at the same time.")
if options.reftest and (options.unitTest or options.fontTest):
self.error("--reftest and --unitTest/--fontTest must not be specified at the same time.")
if options.masterMode and options.manifestFile:
self.error("--masterMode and --manifestFile must not be specified at the same time.")
if not options.manifestFile:
@ -132,6 +135,14 @@ class TestHandlerBase(BaseHTTPRequestHandler):
if VERBOSE:
BaseHTTPRequestHandler.log_request(code, size)
def handle_one_request(self):
try:
BaseHTTPRequestHandler.handle_one_request(self)
except socket.error, v:
# Ignoring connection reset by peer exceptions
if v[0] != errno.ECONNRESET:
raise
def sendFile(self, path, ext):
self.send_response(200)
self.send_header("Content-Type", MIMEs[ext])
@ -142,6 +153,7 @@ class TestHandlerBase(BaseHTTPRequestHandler):
def do_GET(self):
url = urlparse(self.path)
# Ignore query string
path, _ = urllib.unquote_plus(url.path), url.query
path = os.path.abspath(os.path.realpath(DOC_ROOT + os.sep + path))
@ -157,7 +169,7 @@ class TestHandlerBase(BaseHTTPRequestHandler):
return
if not (prefix == DOC_ROOT
and os.path.isfile(path)
and os.path.isfile(path)
and ext in MIMEs):
print path
self.send_error(404)
@ -173,15 +185,70 @@ class TestHandlerBase(BaseHTTPRequestHandler):
class UnitTestHandler(TestHandlerBase):
def sendIndex(self, path, query):
print "send index"
def translateFont(self, base64Data):
self.send_response(200)
self.send_header("Content-Type", "text/xml")
self.end_headers()
data = base64.b64decode(base64Data)
taskId = str(uuid.uuid4())
fontPath = 'ttx/' + taskId + '.otf'
resultPath = 'ttx/' + taskId + '.ttx'
with open(fontPath, "wb") as f:
f.write(data)
# When fontTools used directly, we need to snif ttx file
# to check what version of python is used
ttxPath = ''
for path in os.environ["PATH"].split(os.pathsep):
if os.path.isfile(path + os.sep + "ttx"):
ttxPath = path + os.sep + "ttx"
break
if ttxPath == '':
self.wfile.write("<error>TTX was not found</error>")
return
ttxRunner = ''
with open(ttxPath, "r") as f:
firstLine = f.readline()
if firstLine[:2] == '#!' and firstLine.find('python') > -1:
ttxRunner = firstLine[2:].strip()
with open(os.devnull, "w") as fnull:
if ttxRunner != '':
result = subprocess.call([ttxRunner, ttxPath, fontPath], stdout = fnull)
else:
result = subprocess.call([ttxPath, fontPath], stdout = fnull)
os.remove(fontPath)
if not os.path.isfile(resultPath):
self.wfile.write("<error>Output was not generated</error>")
return
with open(resultPath, "rb") as f:
self.wfile.write(f.read())
os.remove(resultPath)
return
def do_POST(self):
url = urlparse(self.path)
numBytes = int(self.headers['Content-Length'])
content = self.rfile.read(numBytes)
# Process special utility requests
if url.path == '/ttx':
self.translateFont(content)
return
self.send_response(200)
self.send_header('Content-Type', 'text/plain')
self.end_headers()
url = urlparse(self.path)
result = json.loads(self.rfile.read(numBytes))
result = json.loads(content)
browser = result['browser']
UnitTestState.lastPost[browser] = int(time.time())
if url.path == "/tellMeToQuit":
@ -563,7 +630,7 @@ def checkEq(task, results, browser, masterMode):
eq = (ref == snapshot)
if not eq:
print 'TEST-UNEXPECTED-FAIL | ', taskType, taskId, '| in', browser, '| rendering of page', page + 1, '!= reference rendering'
print 'TEST-UNEXPECTED-FAIL |', taskType, taskId, '| in', browser, '| rendering of page', page + 1, '!= reference rendering'
if not State.eqLog:
State.eqLog = open(EQLOG_FILE, 'w')
@ -592,7 +659,7 @@ def checkEq(task, results, browser, masterMode):
of.close()
if passed:
print 'TEST-PASS | ', taskType, ' test', task['id'], '| in', browser
print 'TEST-PASS |', taskType, 'test', task['id'], '| in', browser
def checkFBF(task, results, browser):
round0, round1 = results[0], results[1]
@ -693,10 +760,10 @@ def runTests(options, browsers):
print "\nStarting reftest harness to examine %d eq test failures." % State.numEqFailures
startReftest(browsers[0], options)
def runUnitTests(options, browsers):
def runUnitTests(options, browsers, url, name):
t1 = time.time()
try:
startBrowsers(browsers, options, '/test/unit/unit_test.html')
startBrowsers(browsers, options, url)
while UnitTestState.browsersRunning > 0:
for b in UnitTestState.lastPost:
if UnitTestState.lastPost[b] != None and int(time.time()) - UnitTestState.lastPost[b] > BROWSER_TIMEOUT:
@ -708,14 +775,14 @@ def runUnitTests(options, browsers):
print ''
print 'Ran', UnitTestState.numRun, 'tests'
if UnitTestState.numErrors > 0:
print 'OHNOES! Some tests failed!'
print 'OHNOES! Some', name, 'tests failed!'
print ' ', UnitTestState.numErrors, 'of', UnitTestState.numRun, 'failed'
else:
print 'All unit tests passed.'
print 'All', name, 'tests passed.'
finally:
teardownBrowsers(browsers)
t2 = time.time()
print "Unit test Runtime was", int(t2 - t1), "seconds"
print '', name, 'tests runtime was', int(t2 - t1), 'seconds'
def main():
optionParser = TestOptions()
@ -724,7 +791,7 @@ def main():
if options == None:
sys.exit(1)
if options.unitTest:
if options.unitTest or options.fontTest:
httpd = TestServer((SERVER_HOST, options.port), UnitTestHandler)
httpd_thread = threading.Thread(target=httpd.serve_forever)
httpd_thread.setDaemon(True)
@ -732,7 +799,10 @@ def main():
browsers = setUpUnitTests(options)
if len(browsers) > 0:
runUnitTests(options, browsers)
if options.unitTest:
runUnitTests(options, browsers, '/test/unit/unit_test.html', 'unit')
if options.fontTest:
runUnitTests(options, browsers, '/test/font/font_test.html', 'font')
else:
httpd = TestServer((SERVER_HOST, options.port), PDFTestHandler)
httpd.masterMode = options.masterMode

19
test/ttx/README.md Normal file
View File

@ -0,0 +1,19 @@
This folder is a place for temporary files generated by ttx
# About TTX Installation
The numpy module is required -- use "easy_install numpy" to install it.
Download and extract fonttools from http://sourceforge.net/projects/fonttools/ in any folder on your computer.
From the font tools directory run "python setup.py install" from the command line.
# TTX for Mac Change
On Mac OSX, if you are getting error message related to "/Library/Python/2.7/site-packages/FontTools/fontTools/ttLib/macUtils.py", line 18, in MyOpenResFile, use the following patch to change the fonttools
https://github.com/mcolyer/fonttools/commit/e732bd3ba63c51df9aed903eb2147fa1af1bfdc2
# TTX for Windows Change
On Windows, if ttx generate an exception, it waits for a key to be pressed. Pleaase change "/c/mozilla-build/python/Lib/site-packages/Font-Tools/fontTools/ttx.py" file: replace the waitForKeyPress function body with just 'return'.