diff --git a/test.py b/test.py new file mode 100644 index 000000000..46d30fef5 --- /dev/null +++ b/test.py @@ -0,0 +1,175 @@ +import json, os, sys, subprocess +from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer + +ANAL = True +VERBOSE = False + +MIMEs = { + '.css': 'text/css', + '.html': 'text/html', + '.js': 'application/json', + '.json': 'application/json', + '.pdf': 'application/pdf', + '.xhtml': 'application/xhtml+xml', +} + +class State: + browsers = [ ] + manifest = { } + taskResults = { } + remaining = 0 + results = { } + done = False + +class Result: + def __init__(self, snapshot, failure): + self.snapshot = snapshot + self.failure = failure + + +class PDFTestHandler(BaseHTTPRequestHandler): + # Disable annoying noise by default + def log_request(code=0, size=0): + if VERBOSE: + BaseHTTPRequestHandler.log_request(code, size) + + def do_GET(self): + cwd = os.getcwd() + path = os.path.abspath(os.path.realpath(cwd + os.sep + self.path)) + cwd = os.path.abspath(cwd) + prefix = os.path.commonprefix(( path, cwd )) + _, ext = os.path.splitext(path) + + if not (prefix == cwd + and os.path.isfile(path) + and ext in MIMEs): + self.send_error(404) + return + + if 'Range' in self.headers: + # TODO for fetch-as-you-go + self.send_error(501) + return + + self.send_response(200) + self.send_header("Content-Type", MIMEs[ext]) + self.end_headers() + + # Sigh, os.sendfile() plz + f = open(path) + self.wfile.write(f.read()) + f.close() + + + def do_POST(self): + numBytes = int(self.headers['Content-Length']) + + self.send_response(200) + self.send_header('Content-Type', 'text/plain') + self.end_headers() + + result = json.loads(self.rfile.read(numBytes)) + browser = 'firefox4' + id, failure, round, page, snapshot = result['id'], result['failure'], result['round'], result['page'], result['snapshot'] + taskResults = State.taskResults[browser][id] + taskResults[round][page - 1] = Result(snapshot, failure) + + if result['taskDone']: + check(State.manifest[id], taskResults, browser) + State.remaining -= 1 + + State.done = (0 == State.remaining) + + +def set_up(): + # Only serve files from a pdf.js clone + assert not ANAL or os.path.isfile('pdf.js') and os.path.isdir('.git') + + testBrowsers = [ b for b in + ( 'firefox4', ) +#'chrome12', 'chrome13', 'firefox5', 'firefox6','opera11' ): + if os.access(b, os.R_OK | os.X_OK) ] + + mf = open('test_manifest.json') + manifestList = json.load(mf) + mf.close() + + for b in testBrowsers: + State.taskResults[b] = { } + for item in manifestList: + id, rounds = item['id'], int(item['rounds']) + State.manifest[id] = item + taskResults = [ ] + for r in xrange(rounds): + taskResults.append([ None ] * 100) + State.taskResults[b][id] = taskResults + + State.remaining = len(manifestList) + + for b in testBrowsers: + print 'Launching', b + subprocess.Popen(( os.path.abspath(os.path.realpath(b)), + 'http://localhost:8080/test_slave.html' )) + + +def check(task, results, browser): + failed = False + for r in xrange(len(results)): + pageResults = results[r] + for p in xrange(len(pageResults)): + pageResult = pageResults[p] + if pageResult is None: + continue + failure = pageResult.failure + if failure: + failed = True + print 'TEST-UNEXPECTED-FAIL | test failed', task['id'], '| in', browser, '| page', p + 1, 'round', r, '|', failure + + if failed: + return + + kind = task['type'] + if '==' == kind: + checkEq(task, results, browser) + elif 'fbf' == kind: + checkFBF(task, results, browser) + elif 'load' == kind: + checkLoad(task, results, browser) + else: + assert 0 and 'Unknown test type' + + +def checkEq(task, results, browser): + print ' !!! [TODO: == tests] !!!' + print 'TEST-PASS | == test', task['id'], '| in', browser + + +printed = [False] + +def checkFBF(task, results, browser): + round0, round1 = results[0], results[1] + assert len(round0) == len(round1) + + for page in xrange(len(round1)): + r0Page, r1Page = round0[page], round1[page] + if r0Page is None: + break + if r0Page.snapshot != r1Page.snapshot: + print 'TEST-UNEXPECTED-FAIL | forward-back-forward test', task['id'], '| in', browser, '| first rendering of page', page + 1, '!= second' + print 'TEST-PASS | forward-back-forward test', task['id'], '| in', browser + + +def checkLoad(task, results, browser): + # Load just checks for absence of failure, so if we got here the + # test has passed + print 'TEST-PASS | load test', task['id'], '| in', browser + + +def main(): + set_up() + server = HTTPServer(('127.0.0.1', 8080), PDFTestHandler) + while not State.done: + server.handle_request() + +if __name__ == '__main__': + main() diff --git a/test_manifest.json b/test_manifest.json new file mode 100644 index 000000000..2f45a026c --- /dev/null +++ b/test_manifest.json @@ -0,0 +1,17 @@ +[ + { "id": "tracemonkey-==", + "file": "tests/tracemonkey.pdf", + "rounds": 1, + "type": "==" + }, + { "id": "tracemonkey-fbf", + "file": "tests/tracemonkey.pdf", + "rounds": 2, + "type": "fbf" + }, + { "id": "html5-canvas-cheat-sheet-load", + "file": "tests/canvas.pdf", + "rounds": 1, + "type": "load" + } +] diff --git a/test_slave.html b/test_slave.html new file mode 100644 index 000000000..c560d90d0 --- /dev/null +++ b/test_slave.html @@ -0,0 +1,149 @@ +<html> +<head> + <title>pdf.js test slave</title> + <script type="text/javascript" src="pdf.js"></script> + <script type="text/javascript" src="fonts.js"></script> + <script type="text/javascript" src="glyphlist.js"></script> + <script type="application/javascript"> +var canvas, currentTask, currentTaskIdx, failure, manifest, pdfDoc, stdout; + +function load() { + canvas = document.createElement("canvas"); + // 8.5x11in @ 100% ... XXX need something better here + canvas.width = 816; + canvas.height = 1056; + canvas.mozOpaque = true; + stdout = document.getElementById("stdout"); + + log("Fetching manifest ..."); + + var r = new XMLHttpRequest(); + r.open("GET", "test_manifest.json", false); + r.onreadystatechange = function(e) { + if (r.readyState == 4) { + log("done\n"); + + manifest = JSON.parse(r.responseText); + currentTaskIdx = 0, nextTask(); + } + }; + r.send(null); +} + +function nextTask() { + if (currentTaskIdx == manifest.length) { + return done(); + } + currentTask = manifest[currentTaskIdx]; + currentTask.round = 0; + + log("Loading file "+ currentTask.file +"\n"); + + var r = new XMLHttpRequest(); + r.open("GET", currentTask.file); + r.mozResponseType = r.responseType = "arraybuffer"; + r.onreadystatechange = function() { + if (r.readyState == 4) { + var data = r.mozResponseArrayBuffer || r.mozResponse || + r.responseArrayBuffer || r.response; + pdfDoc = new PDFDoc(new Stream(data)); + currentTask.pageNum = 1, nextPage(); + } + }; + r.send(null); +} + +function nextPage() { + if (currentTask.pageNum > pdfDoc.numPages) { + if (++currentTask.round < currentTask.rounds) { + log(" Round "+ (1 + currentTask.round) +"\n"); + currentTask.pageNum = 1; + } else { + ++currentTaskIdx, nextTask(); + return; + } + } + + failure = ''; + log(" drawing page "+ currentTask.pageNum +"..."); + + currentPage = pdfDoc.getPage(currentTask.pageNum); + + var ctx = canvas.getContext("2d"); + clear(ctx); + + var fonts = []; + var gfx = new CanvasGraphics(ctx); + try { + currentPage.compile(gfx, fonts); + } catch(e) { + failure = 'compile: '+ e.toString(); + } + + // TODO load fonts + setTimeout(function() { + if (!failure) { + try { + currentPage.display(gfx); + } catch(e) { + failure = 'render: '+ e.toString(); + } + } + currentTask.taskDone = (currentTask.pageNum == pdfDoc.numPages + && (1 + currentTask.round) == currentTask.rounds); + sendTaskResult(canvas.toDataURL("image/png")); + log("done"+ (failure ? " (failed!)" : "") +"\n"); + + ++currentTask.pageNum, nextPage(); + }, + 0 + ); +} + +function done() { + log("Done!\n"); + setTimeout(function() { + document.body.innerHTML = "Tests are finished. <h1>CLOSE ME!</h1>"; + window.close(); + }, + 100 + ); +} + +function sendTaskResult(snapshot) { + var result = { id: currentTask.id, + taskDone: currentTask.taskDone, + failure: failure, + file: currentTask.file, + round: currentTask.round, + page: currentTask.pageNum, + snapshot: snapshot }; + + var r = new XMLHttpRequest(); + // (The POST URI is ignored atm.) + r.open("POST", "submit_task_results", false); + r.setRequestHeader("Content-Type", "application/json"); + // XXX async + r.send(JSON.stringify(result)); +} + +function clear(ctx) { + var ctx = canvas.getContext("2d"); + ctx.save(); + ctx.fillStyle = "rgb(255, 255, 255)"; + ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.restore(); +} + +function log(str) { + stdout.innerHTML += str; + window.scrollTo(0, stdout.getBoundingClientRect().bottom); +} + </script> +</head> + +<body onload="load();"> + <pre id="stdout"></pre> +</body> + +</html> diff --git a/tests/canvas.pdf b/tests/canvas.pdf new file mode 100644 index 000000000..900d8af23 Binary files /dev/null and b/tests/canvas.pdf differ diff --git a/tests/tracemonkey.pdf b/tests/tracemonkey.pdf new file mode 100644 index 000000000..65570184a Binary files /dev/null and b/tests/tracemonkey.pdf differ