From 58769fd3b9c518485475ac6d06ce821729a3a979 Mon Sep 17 00:00:00 2001 From: Tim van der Meij Date: Fri, 15 May 2015 14:02:56 +0200 Subject: [PATCH 1/5] Refactor test slave - Improved names of elements - Easier scrolling code --- test/driver.js | 17 +++++++---------- test/test_slave.html | 43 ++++++++++++++++++------------------------- 2 files changed, 25 insertions(+), 35 deletions(-) diff --git a/test/driver.js b/test/driver.js index 88be8a7ff..2cc69b6d6 100644 --- a/test/driver.js +++ b/test/driver.js @@ -54,7 +54,7 @@ window.load = function load() { var delay = params.delay || 0; canvas = document.createElement('canvas'); - stdout = document.getElementById('stdout'); + stdout = document.getElementById('output'); info('User Agent: ' + navigator.userAgent); log('load...\n'); @@ -93,7 +93,7 @@ function cleanup(callback) { var ownerNode = styleSheet.ownerNode; ownerNode.parentNode.removeChild(ownerNode); } - var guard = document.getElementById('content-end'); + var guard = document.getElementById('end'); var body = document.body; while (body.lastChild !== guard) { body.removeChild(body.lastChild); @@ -369,7 +369,7 @@ function quitApp() { function done() { if (inFlightRequests > 0) { - document.getElementById('inFlightCount').innerHTML = inFlightRequests; + document.getElementById('inflight').innerHTML = inFlightRequests; setTimeout(done, 100); } else { setTimeout(quitApp, 100); @@ -417,7 +417,7 @@ function send(url, message, callback) { } } }; - document.getElementById('inFlightCount').innerHTML = inFlightRequests++; + document.getElementById('inflight').innerHTML = inFlightRequests++; r.send(message); } @@ -433,11 +433,8 @@ function clear(ctx) { ctx.clearRect(0, 0, canvas.width, canvas.height); } -/* Auto-scroll if the scrollbar is near the bottom, otherwise do nothing. */ -function checkScrolling() { - if ((stdout.scrollHeight - stdout.scrollTop) <= stdout.offsetHeight) { - stdout.scrollTop = stdout.scrollHeight; - } +function scroll() { + window.scrollTo(0, document.body.scrollHeight); } function log(str) { @@ -448,7 +445,7 @@ function log(str) { } if (str.lastIndexOf('\n') >= 0) { - checkScrolling(); + scroll(); } } diff --git a/test/test_slave.html b/test/test_slave.html index 83a7abedd..0bee4d21a 100644 --- a/test/test_slave.html +++ b/test/test_slave.html @@ -1,6 +1,6 @@ - - pdf.js test slave - - - - - - - - - + PDF.js test slave + + + + + + + + + - - - -

-    

Inflight requests:

-
- - +

+    

Inflight requests:

+
+ From d59660cfa6664369e1ffc4c283a3695517a79ceb Mon Sep 17 00:00:00 2001 From: Tim van der Meij Date: Fri, 15 May 2015 14:24:40 +0200 Subject: [PATCH 2/5] Clean up the test driver - Remove a hack for Chrome on Windows because we do not run Chrome on the Windows bot anymore, so it added unneeded complexity. - Merge nextTask and continueNextTask functions. nextTask did nothing more than calling cleanup. This change also allows us to remove the callback for that function. - Remove unnecessary one-line functions. --- test/driver.js | 43 ++++++++++--------------------------------- 1 file changed, 10 insertions(+), 33 deletions(-) diff --git a/test/driver.js b/test/driver.js index 2cc69b6d6..769c1caec 100644 --- a/test/driver.js +++ b/test/driver.js @@ -31,9 +31,6 @@ var appPath, masterMode, browser, canvas, dummyCanvas, currentTaskIdx, manifest, stdout; var inFlightRequests = 0; -// Chrome for Windows locks during testing on low end machines -var letItCooldown = /Windows.*?Chrom/i.test(navigator.userAgent); - function queryParams() { var qs = window.location.search.substring(1); var kvs = qs.split('&'); @@ -83,7 +80,7 @@ window.load = function load() { }, delay); }; -function cleanup(callback) { +function cleanup() { // Clear out all the stylesheets since a new one is created for each font. while (document.styleSheets.length > 0) { var styleSheet = document.styleSheets[0]; @@ -106,11 +103,6 @@ function cleanup(callback) { delete manifest[i].pdfDoc; } } - if (letItCooldown) { - setTimeout(callback, 500); - } else { - callback(); - } } function exceptionToString(e) { @@ -124,10 +116,8 @@ function exceptionToString(e) { } function nextTask() { - cleanup(continueNextTask); -} + cleanup(); -function continueNextTask() { if (currentTaskIdx === manifest.length) { done(); return; @@ -177,14 +167,6 @@ function getLastPageNum(task) { return lastPageNum; } -function isLastPage(task) { - return task.pageNum > getLastPageNum(task); -} - -function canvasToDataURL() { - return canvas.toDataURL('image/png'); -} - function NullTextLayerBuilder() { } NullTextLayerBuilder.prototype = { @@ -239,7 +221,8 @@ function nextPage(task, loadError) { var failure = loadError || ''; if (!task.pdfDoc) { - sendTaskResult(canvasToDataURL(), task, failure, function () { + var dataUrl = canvas.toDataURL('image/png'); + sendTaskResult(dataUrl, task, failure, function () { log('done' + (failure ? ' (failed !: ' + failure + ')' : '') + '\n'); ++currentTaskIdx; nextTask(); @@ -247,7 +230,7 @@ function nextPage(task, loadError) { return; } - if (isLastPage(task)) { + if (task.pageNum > getLastPageNum(task)) { if (++task.round < task.rounds) { log(' Round ' + (1 + task.round) + '\n'); task.pageNum = task.firstPage || 1; @@ -337,7 +320,8 @@ function nextPage(task, loadError) { function snapshotCurrentPage(task, failure) { log('done, snapshotting... '); - sendTaskResult(canvasToDataURL(), task, failure, function () { + var dataUrl = canvas.toDataURL('image/png'); + sendTaskResult(dataUrl, task, failure, function () { log('done' + (failure ? ' (failed !: ' + failure + ')' : '') + '\n'); ++task.pageNum; @@ -409,11 +393,7 @@ function send(url, message, callback) { }); } if (callback) { - if (letItCooldown) { - setTimeout(callback, 100); - } else { - callback(); - } + callback(); } } }; @@ -433,10 +413,6 @@ function clear(ctx) { ctx.clearRect(0, 0, canvas.width, canvas.height); } -function scroll() { - window.scrollTo(0, document.body.scrollHeight); -} - function log(str) { if (stdout.insertAdjacentHTML) { stdout.insertAdjacentHTML('BeforeEnd', str); @@ -445,7 +421,8 @@ function log(str) { } if (str.lastIndexOf('\n') >= 0) { - scroll(); + // Scroll to the bottom of the page + window.scrollTo(0, document.body.scrollHeight); } } From ae8748edfbfe7e2e21f035ec6699783368b5c8f3 Mon Sep 17 00:00:00 2001 From: Tim van der Meij Date: Fri, 15 May 2015 15:29:43 +0200 Subject: [PATCH 3/5] Refactor test driver to be more class-like - Extract NullTextLayerBuilder and SimpleTextLayerBuilder from the driver code and create classes for them. - Move options to the constructor and pass in HTML elements instead of getting them in the driver. - Remove unused masterMode variable. - Minor method name updates. The rest is only moving/indenting existing code and adding 'this'. --- test/driver.js | 805 +++++++++++++++++++++++-------------------- test/test_slave.html | 8 +- 2 files changed, 433 insertions(+), 380 deletions(-) diff --git a/test/driver.js b/test/driver.js index 769c1caec..8149124a9 100644 --- a/test/driver.js +++ b/test/driver.js @@ -18,412 +18,461 @@ 'use strict'; -/* - * A Test Driver for PDF.js +/** + * @class */ -(function DriverClosure() { +var NullTextLayerBuilder = (function NullTextLayerBuilderClosure() { + /** + * @constructs NullTextLayerBuilder + */ + function NullTextLayerBuilder() {} -PDFJS.enableStats = true; -PDFJS.cMapUrl = '../external/bcmaps/'; -PDFJS.cMapPacked = true; + NullTextLayerBuilder.prototype = { + beginLayout: function NullTextLayerBuilder_BeginLayout() {}, + endLayout: function NullTextLayerBuilder_EndLayout() {}, + appendText: function NullTextLayerBuilder_AppendText() {} + }; -var appPath, masterMode, browser, canvas, dummyCanvas, currentTaskIdx, - manifest, stdout; -var inFlightRequests = 0; + return NullTextLayerBuilder; +})(); -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]); +/** + * @class + */ +var SimpleTextLayerBuilder = (function SimpleTextLayerBuilderClosure() { + /** + * @constructs SimpleTextLayerBuilder + */ + function SimpleTextLayerBuilder(ctx, viewport) { + this.ctx = ctx; + this.viewport = viewport; + this.textCounter = 0; } - return params; -} -window.load = function load() { - var params = queryParams(); - browser = params.browser; - var manifestFile = params.manifestFile; - appPath = params.path; - masterMode = params.masterMode === 'True'; - var delay = params.delay || 0; + SimpleTextLayerBuilder.prototype = { + appendText: function SimpleTextLayerBuilder_AppendText(geom, styles) { + var style = styles[geom.fontName]; + var ctx = this.ctx, viewport = this.viewport; + var tx = PDFJS.Util.transform(this.viewport.transform, geom.transform); + var angle = Math.atan2(tx[1], tx[0]); + var fontHeight = Math.sqrt((tx[2] * tx[2]) + (tx[3] * tx[3])); + var fontAscent = (style.ascent ? style.ascent * fontHeight : + (style.descent ? (1 + style.descent) * fontHeight : fontHeight)); - canvas = document.createElement('canvas'); - stdout = document.getElementById('output'); + ctx.save(); + ctx.beginPath(); + ctx.strokeStyle = 'red'; + ctx.fillStyle = 'yellow'; + ctx.translate(tx[4] + (fontAscent * Math.sin(angle)), + tx[5] - (fontAscent * Math.cos(angle))); + ctx.rotate(angle); + ctx.rect(0, 0, geom.width * viewport.scale, geom.height * viewport.scale); + ctx.stroke(); + ctx.fill(); + ctx.restore(); + ctx.font = fontHeight + 'px ' + style.fontFamily; + ctx.fillStyle = 'black'; + ctx.fillText(geom.str, tx[4], tx[5]); - info('User Agent: ' + navigator.userAgent); - log('load...\n'); + this.textCounter++; + }, - log('Harness thinks this browser is "' + browser + '" with path "' + - appPath + '"\n'); - log('Fetching manifest "' + manifestFile + '"... '); - - var r = new XMLHttpRequest(); - r.open('GET', manifestFile, false); - r.onreadystatechange = function loadOnreadystatechange(e) { - if (r.readyState === 4) { - log('done\n'); - manifest = JSON.parse(r.responseText); - currentTaskIdx = 0; - nextTask(); + setTextContent: + function SimpleTextLayerBuilder_SetTextContent(textContent) { + this.ctx.save(); + var textItems = textContent.items; + for (var i = 0, ii = textItems.length; i < ii; i++) { + this.appendText(textItems[i], textContent.styles); + } + this.ctx.restore(); } }; - if (delay) { - log('\nDelaying for ' + delay + 'ms...\n'); - } - // When gathering the stats the numbers seem to be more reliable if the - // browser is given more time to startup. - setTimeout(function() { - r.send(null); - }, delay); -}; -function cleanup() { - // Clear out all the stylesheets since a new one is created for each font. - while (document.styleSheets.length > 0) { - var styleSheet = document.styleSheets[0]; - while (styleSheet.cssRules.length > 0) { - styleSheet.deleteRule(0); - } - var ownerNode = styleSheet.ownerNode; - ownerNode.parentNode.removeChild(ownerNode); - } - var guard = document.getElementById('end'); - var body = document.body; - while (body.lastChild !== guard) { - body.removeChild(body.lastChild); + return SimpleTextLayerBuilder; +})(); + +/** + * @typedef {Object} DriverOptions + * @property {HTMLPreElement} output - Container for all output messages. + * @property {HTMLSpanElement} inflight - Field displaying the number of + * inflight requests. + * @property {HTMLDivElement} end - Container for a completion message. + */ + +/** + * @class + */ +var Driver = (function DriverClosure() { + /** + * @constructs Driver + * @param {DriverOptions} options + */ + function Driver(options) { + // Configure the global PDFJS object + PDFJS.workerSrc = '../src/worker_loader.js'; + PDFJS.cMapPacked = true; + PDFJS.cMapUrl = '../external/bcmaps/'; + PDFJS.enableStats = true; + + // Set the passed options + this.output = options.output; + this.inflight = options.inflight; + this.end = options.end; + + // Set parameters from the query string + var parameters = this._getQueryStringParameters(); + this.browser = parameters.browser; + this.manifestFile = parameters.manifestFile; + this.appPath = parameters.path; + this.delay = (parameters.delay | 0) || 0; + this.inFlightRequests = 0; + + // Create a working canvas + this.canvas = document.createElement('canvas'); } - // Wipe out the link to the pdfdoc so it can be GC'ed. - for (var i = 0; i < manifest.length; i++) { - if (manifest[i].pdfDoc) { - manifest[i].pdfDoc.destroy(); - delete manifest[i].pdfDoc; - } - } -} + Driver.prototype = { + _getQueryStringParameters: function Driver_getQueryStringParameters() { + 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; + }, -function exceptionToString(e) { - if (typeof e !== 'object') { - return String(e); - } - if (!('message' in e)) { - return JSON.stringify(e); - } - return e.message + ('stack' in e ? ' at ' + e.stack.split('\n')[0] : ''); -} + run: function Driver_run() { + this._info('User Agent: ' + navigator.userAgent); + this._log('load...\n'); -function nextTask() { - cleanup(); + this._log('Harness thinks this browser is "' + this.browser + + '" with path "' + this.appPath + '"\n'); + this._log('Fetching manifest "' + this.manifestFile + '"... '); - if (currentTaskIdx === manifest.length) { - done(); - return; - } - var task = manifest[currentTaskIdx]; - task.round = 0; - task.stats = {times: []}; - - log('Loading file "' + task.file + '"\n'); - - var absoluteUrl = combineUrl(window.location.href, task.file); - var failure; - function continuation() { - task.pageNum = task.firstPage || 1; - nextPage(task, failure); - } - - PDFJS.disableRange = task.disableRange; - PDFJS.disableAutoFetch = !task.enableAutoFetch; - try { - var promise = PDFJS.getDocument({ - url: absoluteUrl, - password: task.password - }); - promise.then(function(doc) { - task.pdfDoc = doc; - continuation(); - }, function(e) { - failure = 'load PDF doc : ' + e; - continuation(); - }); - return; - } catch (e) { - failure = 'load PDF doc : ' + exceptionToString(e); - } - continuation(); -} - -function getLastPageNum(task) { - if (!task.pdfDoc) { - return task.firstPage || 1; - } - var lastPageNum = task.lastPage || 0; - if (!lastPageNum || lastPageNum > task.pdfDoc.numPages) { - lastPageNum = task.pdfDoc.numPages; - } - return lastPageNum; -} - -function NullTextLayerBuilder() { -} -NullTextLayerBuilder.prototype = { - beginLayout: function NullTextLayerBuilder_BeginLayout() {}, - endLayout: function NullTextLayerBuilder_EndLayout() {}, - appendText: function NullTextLayerBuilder_AppendText() {} -}; - -function SimpleTextLayerBuilder(ctx, viewport) { - this.ctx = ctx; - this.viewport = viewport; - this.textCounter = 0; -} -SimpleTextLayerBuilder.prototype = { - appendText: function SimpleTextLayerBuilder_AppendText(geom, styles) { - var style = styles[geom.fontName]; - var ctx = this.ctx, viewport = this.viewport; - var tx = PDFJS.Util.transform(this.viewport.transform, geom.transform); - var angle = Math.atan2(tx[1], tx[0]); - var fontHeight = Math.sqrt((tx[2] * tx[2]) + (tx[3] * tx[3])); - var fontAscent = (style.ascent ? style.ascent * fontHeight : - (style.descent ? (1 + style.descent) * fontHeight : fontHeight)); - ctx.save(); - ctx.beginPath(); - ctx.strokeStyle = 'red'; - ctx.fillStyle = 'yellow'; - ctx.translate(tx[4] + (fontAscent * Math.sin(angle)), - tx[5] - (fontAscent * Math.cos(angle))); - ctx.rotate(angle); - ctx.rect(0, 0, geom.width * viewport.scale, geom.height * viewport.scale); - ctx.stroke(); - ctx.fill(); - ctx.restore(); - ctx.font = fontHeight + 'px ' + style.fontFamily; - ctx.fillStyle = 'black'; - ctx.fillText(geom.str, tx[4], tx[5]); - - this.textCounter++; - }, - setTextContent: function SimpleTextLayerBuilder_SetTextContent(textContent) { - this.ctx.save(); - var textItems = textContent.items; - for (var i = 0; i < textItems.length; i++) { - this.appendText(textItems[i], textContent.styles); - } - - this.ctx.restore(); - } -}; - -function nextPage(task, loadError) { - var failure = loadError || ''; - - if (!task.pdfDoc) { - var dataUrl = canvas.toDataURL('image/png'); - sendTaskResult(dataUrl, task, failure, function () { - log('done' + (failure ? ' (failed !: ' + failure + ')' : '') + '\n'); - ++currentTaskIdx; - nextTask(); - }); - return; - } - - if (task.pageNum > getLastPageNum(task)) { - if (++task.round < task.rounds) { - log(' Round ' + (1 + task.round) + '\n'); - task.pageNum = task.firstPage || 1; - } else { - ++currentTaskIdx; - nextTask(); - return; - } - } - - if (task.skipPages && task.skipPages.indexOf(task.pageNum) >= 0) { - log(' skipping page ' + task.pageNum + '/' + task.pdfDoc.numPages + - '... '); - // empty the canvas - canvas.width = 1; - canvas.height = 1; - clear(canvas.getContext('2d')); - - snapshotCurrentPage(task, ''); - return; - } - - if (!failure) { - try { - log(' loading page ' + task.pageNum + '/' + task.pdfDoc.numPages + - '... '); - var ctx = canvas.getContext('2d'); - task.pdfDoc.getPage(task.pageNum).then(function(page) { - var pdfToCssUnitsCoef = 96.0 / 72.0; - var viewport = page.getViewport(pdfToCssUnitsCoef); - canvas.width = viewport.width; - canvas.height = viewport.height; - clear(ctx); - - var drawContext, textLayerBuilder; - var resolveInitPromise; - var initPromise = new Promise(function (resolve) { - resolveInitPromise = resolve; - }); - if (task.type === 'text') { - // using dummy canvas for pdf context drawing operations - if (!dummyCanvas) { - dummyCanvas = document.createElement('canvas'); - } - drawContext = dummyCanvas.getContext('2d'); - // ... text builder will draw its content on the test canvas - textLayerBuilder = new SimpleTextLayerBuilder(ctx, viewport); - - page.getTextContent().then(function(textContent) { - textLayerBuilder.setTextContent(textContent); - resolveInitPromise(); - }); - } else { - drawContext = ctx; - textLayerBuilder = new NullTextLayerBuilder(); - resolveInitPromise(); + var r = new XMLHttpRequest(); + var self = this; + r.open('GET', this.manifestFile, false); + r.onreadystatechange = function loadOnreadystatechange(e) { + if (r.readyState === 4) { + self._log('done\n'); + self.manifest = JSON.parse(r.responseText); + self.currentTask = 0; + self._nextTask(); } - var renderContext = { - canvasContext: drawContext, - viewport: viewport - }; - var completeRender = (function(error) { - page.destroy(); - task.stats = page.stats; - page.stats = new StatTimer(); - snapshotCurrentPage(task, error); + }; + if (this.delay) { + this._log('\nDelaying for ' + this.delay + 'ms...\n'); + } + // When gathering the stats the numbers seem to be more reliable + // if the browser is given more time to startup. + setTimeout(function() { + r.send(null); + }, this.delay); + }, + + _nextTask: function Driver_nextTask() { + this._cleanup(); + + if (this.currentTask === this.manifest.length) { + this._done(); + return; + } + var task = this.manifest[this.currentTask]; + task.round = 0; + task.stats = { times: [] }; + + this._log('Loading file "' + task.file + '"\n'); + + var absoluteUrl = combineUrl(window.location.href, task.file); + var failure; + var self = this; + function continuation() { + task.pageNum = task.firstPage || 1; + self._nextPage(task, failure); + } + + PDFJS.disableRange = task.disableRange; + PDFJS.disableAutoFetch = !task.enableAutoFetch; + try { + PDFJS.getDocument({ + url: absoluteUrl, + password: task.password + }).then(function(doc) { + task.pdfDoc = doc; + continuation(); + }, function(e) { + failure = 'load PDF doc : ' + e; + continuation(); }); - initPromise.then(function () { - page.render(renderContext).promise.then(function() { - completeRender(false); + return; + } catch (e) { + failure = 'load PDF doc : ' + this._exceptionToString(e); + } + continuation(); + }, + + _cleanup: function Driver_cleanup() { + // Clear out all the stylesheets since a new one is created for each font. + while (document.styleSheets.length > 0) { + var styleSheet = document.styleSheets[0]; + while (styleSheet.cssRules.length > 0) { + styleSheet.deleteRule(0); + } + var ownerNode = styleSheet.ownerNode; + ownerNode.parentNode.removeChild(ownerNode); + } + var body = document.body; + while (body.lastChild !== this.end) { + body.removeChild(body.lastChild); + } + + // Wipe out the link to the pdfdoc so it can be GC'ed. + for (var i = 0; i < this.manifest.length; i++) { + if (this.manifest[i].pdfDoc) { + this.manifest[i].pdfDoc.destroy(); + delete this.manifest[i].pdfDoc; + } + } + }, + + _exceptionToString: function Driver_exceptionToString(e) { + if (typeof e !== 'object') { + return String(e); + } + if (!('message' in e)) { + return JSON.stringify(e); + } + return e.message + ('stack' in e ? ' at ' + e.stack.split('\n')[0] : ''); + }, + + _getLastPageNum: function Driver_getLastPageNum(task) { + if (!task.pdfDoc) { + return task.firstPage || 1; + } + var lastPageNum = task.lastPage || 0; + if (!lastPageNum || lastPageNum > task.pdfDoc.numPages) { + lastPageNum = task.pdfDoc.numPages; + } + return lastPageNum; + }, + + _nextPage: function Driver_nextPage(task, loadError) { + var failure = loadError || ''; + var self = this; + + if (!task.pdfDoc) { + var dataUrl = this.canvas.toDataURL('image/png'); + this._sendResult(dataUrl, task, failure, function () { + self._log('done' + (failure ? ' (failed !: ' + failure + ')' : '') + + '\n'); + self.currentTask++; + self._nextTask(); + }); + return; + } + + if (task.pageNum > this._getLastPageNum(task)) { + if (++task.round < task.rounds) { + this._log(' Round ' + (1 + task.round) + '\n'); + task.pageNum = task.firstPage || 1; + } else { + this.currentTask++; + this._nextTask(); + return; + } + } + + if (task.skipPages && task.skipPages.indexOf(task.pageNum) >= 0) { + this._log(' skipping page ' + task.pageNum + '/' + + task.pdfDoc.numPages + '... '); + + // Empty the canvas + this.canvas.width = 1; + this.canvas.height = 1; + this._clearCanvas(this.canvas.getContext('2d')); + + this._snapshot(task, ''); + return; + } + + if (!failure) { + try { + this._log(' loading page ' + task.pageNum + '/' + + task.pdfDoc.numPages + '... '); + var ctx = this.canvas.getContext('2d'); + task.pdfDoc.getPage(task.pageNum).then(function(page) { + var pdfToCssUnitsCoef = 96.0 / 72.0; + var viewport = page.getViewport(pdfToCssUnitsCoef); + self.canvas.width = viewport.width; + self.canvas.height = viewport.height; + self._clearCanvas(ctx); + + var drawContext, textLayerBuilder; + var resolveInitPromise; + var initPromise = new Promise(function (resolve) { + resolveInitPromise = resolve; + }); + if (task.type === 'text') { + // Using a dummy canvas for PDF context drawing operations + if (!self.dummyCanvas) { + self.dummyCanvas = document.createElement('canvas'); + } + drawContext = self.dummyCanvas.getContext('2d'); + // The text builder will draw its content on the test canvas + textLayerBuilder = new SimpleTextLayerBuilder(ctx, viewport); + + page.getTextContent().then(function(textContent) { + textLayerBuilder.setTextContent(textContent); + resolveInitPromise(); + }); + } else { + drawContext = ctx; + textLayerBuilder = new NullTextLayerBuilder(); + resolveInitPromise(); + } + var renderContext = { + canvasContext: drawContext, + viewport: viewport + }; + var completeRender = (function(error) { + page.destroy(); + task.stats = page.stats; + page.stats = new StatTimer(); + self._snapshot(task, error); + }); + initPromise.then(function () { + page.render(renderContext).promise.then(function() { + completeRender(false); + }, + function(error) { + completeRender('render : ' + error); + }); + }); }, function(error) { - completeRender('render : ' + error); + self._snapshot(task, 'render : ' + error); }); - }); - }, - function(error) { - snapshotCurrentPage(task, 'render : ' + error); + } catch (e) { + failure = 'page setup : ' + this._exceptionToString(e); + this._snapshot(task, failure); + } + } + }, + + _clearCanvas: function Driver_clearCanvas() { + var ctx = this.canvas.getContext('2d'); + ctx.beginPath(); + ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); + }, + + _snapshot: function Driver_snapshot(task, failure) { + var self = this; + this._log('done, snapshotting... '); + + var dataUrl = this.canvas.toDataURL('image/png'); + this._sendResult(dataUrl, task, failure, function () { + self._log('done' + (failure ? ' (failed !: ' + failure + ')' : '') + + '\n'); + task.pageNum++; + self._nextPage(task); }); - } catch (e) { - failure = 'page setup : ' + exceptionToString(e); - snapshotCurrentPage(task, failure); - } - } -} + }, -function snapshotCurrentPage(task, failure) { - log('done, snapshotting... '); + _quit: function Driver_quit() { + this._log('Done !'); + document.body.innerHTML = 'Tests finished.

Close this window.

' + + document.body.innerHTML; + this._sendQuitRequest(function () { + window.close(); + }); + }, - var dataUrl = canvas.toDataURL('image/png'); - sendTaskResult(dataUrl, task, failure, function () { - log('done' + (failure ? ' (failed !: ' + failure + ')' : '') + '\n'); + _sendQuitRequest: function Driver_sendQuitRequest(callback) { + var r = new XMLHttpRequest(); + r.open('POST', '/tellMeToQuit?path=' + escape(this.appPath), false); + r.onreadystatechange = function sendQuitRequestOnreadystatechange(e) { + if (r.readyState === 4) { + if (callback) { + callback(); + } + } + }; + r.send(null); + }, - ++task.pageNum; - nextPage(task); - }); -} + _info: function Driver_info(message) { + this._send('/info', JSON.stringify({ + browser: this.browser, + message: message + })); + }, -function sendQuitRequest(cb) { - var r = new XMLHttpRequest(); - r.open('POST', '/tellMeToQuit?path=' + escape(appPath), false); - r.onreadystatechange = function sendQuitRequestOnreadystatechange(e) { - if (r.readyState === 4) { - if (cb) { - cb(); + _log: function Driver_log(str) { + if (output.insertAdjacentHTML) { + this.output.insertAdjacentHTML('BeforeEnd', str); + } else { + this.output.innerHTML += str; } + + if (str.lastIndexOf('\n') >= 0) { + // Scroll to the bottom of the page + window.scrollTo(0, document.body.scrollHeight); + } + }, + + _done: function Driver_done() { + if (this.inFlightRequests > 0) { + this.inflight.innerHTML = this.inFlightRequests; + setTimeout(this._done(), 100); + } else { + setTimeout(this._quit(), 100); + } + }, + + _sendResult: function Driver_sendResult(snapshot, task, failure, + callback) { + var result = JSON.stringify({ + browser: this.browser, + id: task.id, + numPages: task.pdfDoc ? + (task.lastPage || task.pdfDoc.numPages) : 0, + lastPageNum: this._getLastPageNum(task), + failure: failure, + file: task.file, + round: task.round, + page: task.pageNum, + snapshot: snapshot, + stats: task.stats.times + }); + this._send('/submit_task_results', result, callback); + }, + + _send: function Driver_send(url, message, callback) { + var self = this; + var r = new XMLHttpRequest(); + r.open('POST', url, true); + r.setRequestHeader('Content-Type', 'application/json'); + r.onreadystatechange = function sendResultOnreadystatechange(e) { + if (r.readyState === 4) { + self.inFlightRequests--; + + // Retry until successful + if (r.status !== 200) { + setTimeout(function() { + self._send(url, message); + }); + } + if (callback) { + callback(); + } + } + }; + this.inflight.innerHTML = this.inFlightRequests++; + r.send(message); } }; - r.send(null); -} -function quitApp() { - log('Done !'); - document.body.innerHTML = 'Tests are finished.

CLOSE ME!

' + - document.body.innerHTML; - sendQuitRequest(function () { - window.close(); - }); -} - -function done() { - if (inFlightRequests > 0) { - document.getElementById('inflight').innerHTML = inFlightRequests; - setTimeout(done, 100); - } else { - setTimeout(quitApp, 100); - } -} - -function sendTaskResult(snapshot, task, failure, callback) { - var result = JSON.stringify({ - browser: browser, - id: task.id, - numPages: task.pdfDoc ? - (task.lastPage || task.pdfDoc.numPages) : 0, - lastPageNum: getLastPageNum(task), - failure: failure, - file: task.file, - round: task.round, - page: task.pageNum, - snapshot: snapshot, - stats: task.stats.times - }); - - send('/submit_task_results', result, callback); -} - -function send(url, message, callback) { - var r = new XMLHttpRequest(); - // (The POST URI is ignored atm.) - r.open('POST', url, true); - r.setRequestHeader('Content-Type', 'application/json'); - r.onreadystatechange = function sendTaskResultOnreadystatechange(e) { - if (r.readyState === 4) { - inFlightRequests--; - // Retry until successful - if (r.status !== 200) { - setTimeout(function() { - send(url, message); - }); - } - if (callback) { - callback(); - } - } - }; - document.getElementById('inflight').innerHTML = inFlightRequests++; - r.send(message); -} - -function info(message) { - send('/info', JSON.stringify({ - browser: browser, - message: message - })); -} - -function clear(ctx) { - ctx.beginPath(); - ctx.clearRect(0, 0, canvas.width, canvas.height); -} - -function log(str) { - if (stdout.insertAdjacentHTML) { - stdout.insertAdjacentHTML('BeforeEnd', str); - } else { - stdout.innerHTML += str; - } - - if (str.lastIndexOf('\n') >= 0) { - // Scroll to the bottom of the page - window.scrollTo(0, document.body.scrollHeight); - } -} - -})(); // DriverClosure + return Driver; +})(); diff --git a/test/test_slave.html b/test/test_slave.html index 0bee4d21a..3a84bd394 100644 --- a/test/test_slave.html +++ b/test/test_slave.html @@ -34,7 +34,11 @@ limitations under the License.
From 07ec736eb9ca3882034fec1db7cf41ee60ee36be Mon Sep 17 00:00:00 2001 From: Tim van der Meij Date: Fri, 15 May 2015 15:54:48 +0200 Subject: [PATCH 4/5] Improve driver code structure - Improve variable names and move variables to the top of the method - Use constants where possible - Only print the delay text if there is an atual delay set - Simplify continuation logic in _nextTask - Do not pass anything to clearCanvas: we can get the context from this.canvas there. - Remove innerHTML and replace it with textContent. Add a comment why insertAdjancentHTML is so important for performance and runtime. - Merge _quit and _sendQuitRequest. --- test/driver.js | 116 +++++++++++++++++++++++-------------------------- 1 file changed, 55 insertions(+), 61 deletions(-) diff --git a/test/driver.js b/test/driver.js index 8149124a9..c67449ba5 100644 --- a/test/driver.js +++ b/test/driver.js @@ -18,6 +18,9 @@ 'use strict'; +var WAITING_TIME = 100; // ms +var PDF_TO_CSS_UNITS = 96.0 / 72.0; + /** * @class */ @@ -133,28 +136,27 @@ var Driver = (function DriverClosure() { Driver.prototype = { _getQueryStringParameters: function Driver_getQueryStringParameters() { - 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]); + var queryString = window.location.search.substring(1); + var values = queryString.split('&'); + var parameters = {}; + for (var i = 0, ii = values.length; i < ii; i++) { + var value = values[i].split('='); + parameters[unescape(value[0])] = unescape(value[1]); } - return params; + return parameters; }, run: function Driver_run() { - this._info('User Agent: ' + navigator.userAgent); - this._log('load...\n'); + var self = this; + this._info('User agent: ' + navigator.userAgent); this._log('Harness thinks this browser is "' + this.browser + '" with path "' + this.appPath + '"\n'); this._log('Fetching manifest "' + this.manifestFile + '"... '); var r = new XMLHttpRequest(); - var self = this; r.open('GET', this.manifestFile, false); - r.onreadystatechange = function loadOnreadystatechange(e) { + r.onreadystatechange = function() { if (r.readyState === 4) { self._log('done\n'); self.manifest = JSON.parse(r.responseText); @@ -162,17 +164,20 @@ var Driver = (function DriverClosure() { self._nextTask(); } }; - if (this.delay) { - this._log('\nDelaying for ' + this.delay + 'ms...\n'); + if (this.delay > 0) { + this._log('\nDelaying for ' + this.delay + ' ms...\n'); } // When gathering the stats the numbers seem to be more reliable - // if the browser is given more time to startup. + // if the browser is given more time to start. setTimeout(function() { r.send(null); }, this.delay); }, _nextTask: function Driver_nextTask() { + var self = this; + var failure = ''; + this._cleanup(); if (this.currentTask === this.manifest.length) { @@ -181,17 +186,12 @@ var Driver = (function DriverClosure() { } var task = this.manifest[this.currentTask]; task.round = 0; + task.pageNum = task.firstPage || 1; task.stats = { times: [] }; this._log('Loading file "' + task.file + '"\n'); var absoluteUrl = combineUrl(window.location.href, task.file); - var failure; - var self = this; - function continuation() { - task.pageNum = task.firstPage || 1; - self._nextPage(task, failure); - } PDFJS.disableRange = task.disableRange; PDFJS.disableAutoFetch = !task.enableAutoFetch; @@ -201,16 +201,16 @@ var Driver = (function DriverClosure() { password: task.password }).then(function(doc) { task.pdfDoc = doc; - continuation(); + self._nextPage(task, failure); }, function(e) { - failure = 'load PDF doc : ' + e; - continuation(); + failure = 'Loading PDF document: ' + e; + self._nextPage(task, failure); }); return; } catch (e) { - failure = 'load PDF doc : ' + this._exceptionToString(e); + failure = 'Loading PDF document: ' + this._exceptionToString(e); } - continuation(); + this._nextPage(task, failure); }, _cleanup: function Driver_cleanup() { @@ -247,20 +247,20 @@ var Driver = (function DriverClosure() { return e.message + ('stack' in e ? ' at ' + e.stack.split('\n')[0] : ''); }, - _getLastPageNum: function Driver_getLastPageNum(task) { + _getLastPageNumber: function Driver_getLastPageNumber(task) { if (!task.pdfDoc) { return task.firstPage || 1; } - var lastPageNum = task.lastPage || 0; - if (!lastPageNum || lastPageNum > task.pdfDoc.numPages) { - lastPageNum = task.pdfDoc.numPages; + var lastPageNumber = task.lastPage || 0; + if (!lastPageNumber || lastPageNumber > task.pdfDoc.numPages) { + lastPageNumber = task.pdfDoc.numPages; } - return lastPageNum; + return lastPageNumber; }, _nextPage: function Driver_nextPage(task, loadError) { - var failure = loadError || ''; var self = this; + var failure = loadError || ''; if (!task.pdfDoc) { var dataUrl = this.canvas.toDataURL('image/png'); @@ -273,7 +273,7 @@ var Driver = (function DriverClosure() { return; } - if (task.pageNum > this._getLastPageNum(task)) { + if (task.pageNum > this._getLastPageNumber(task)) { if (++task.round < task.rounds) { this._log(' Round ' + (1 + task.round) + '\n'); task.pageNum = task.firstPage || 1; @@ -285,13 +285,13 @@ var Driver = (function DriverClosure() { } if (task.skipPages && task.skipPages.indexOf(task.pageNum) >= 0) { - this._log(' skipping page ' + task.pageNum + '/' + + this._log(' Skipping page ' + task.pageNum + '/' + task.pdfDoc.numPages + '... '); // Empty the canvas this.canvas.width = 1; this.canvas.height = 1; - this._clearCanvas(this.canvas.getContext('2d')); + this._clearCanvas(); this._snapshot(task, ''); return; @@ -299,15 +299,14 @@ var Driver = (function DriverClosure() { if (!failure) { try { - this._log(' loading page ' + task.pageNum + '/' + + this._log(' Loading page ' + task.pageNum + '/' + task.pdfDoc.numPages + '... '); var ctx = this.canvas.getContext('2d'); task.pdfDoc.getPage(task.pageNum).then(function(page) { - var pdfToCssUnitsCoef = 96.0 / 72.0; - var viewport = page.getViewport(pdfToCssUnitsCoef); + var viewport = page.getViewport(PDF_TO_CSS_UNITS); self.canvas.width = viewport.width; self.canvas.height = viewport.height; - self._clearCanvas(ctx); + self._clearCanvas(); var drawContext, textLayerBuilder; var resolveInitPromise; @@ -369,7 +368,7 @@ var Driver = (function DriverClosure() { _snapshot: function Driver_snapshot(task, failure) { var self = this; - this._log('done, snapshotting... '); + this._log('Snapshotting... '); var dataUrl = this.canvas.toDataURL('image/png'); this._sendResult(dataUrl, task, failure, function () { @@ -382,21 +381,14 @@ var Driver = (function DriverClosure() { _quit: function Driver_quit() { this._log('Done !'); - document.body.innerHTML = 'Tests finished.

Close this window.

' + - document.body.innerHTML; - this._sendQuitRequest(function () { - window.close(); - }); - }, + this.end.textContent = 'Tests finished. Close this window!'; - _sendQuitRequest: function Driver_sendQuitRequest(callback) { + // Send the quit request var r = new XMLHttpRequest(); r.open('POST', '/tellMeToQuit?path=' + escape(this.appPath), false); - r.onreadystatechange = function sendQuitRequestOnreadystatechange(e) { + r.onreadystatechange = function(e) { if (r.readyState === 4) { - if (callback) { - callback(); - } + window.close(); } }; r.send(null); @@ -409,14 +401,16 @@ var Driver = (function DriverClosure() { })); }, - _log: function Driver_log(str) { - if (output.insertAdjacentHTML) { - this.output.insertAdjacentHTML('BeforeEnd', str); + _log: function Driver_log(message) { + // Using insertAdjacentHTML yields a large performance gain and + // reduces runtime significantly. + if (this.output.insertAdjacentHTML) { + this.output.insertAdjacentHTML('BeforeEnd', message); } else { - this.output.innerHTML += str; + this.output.textContent += message; } - if (str.lastIndexOf('\n') >= 0) { + if (message.lastIndexOf('\n') >= 0) { // Scroll to the bottom of the page window.scrollTo(0, document.body.scrollHeight); } @@ -424,10 +418,10 @@ var Driver = (function DriverClosure() { _done: function Driver_done() { if (this.inFlightRequests > 0) { - this.inflight.innerHTML = this.inFlightRequests; - setTimeout(this._done(), 100); + this.inflight.textContent = this.inFlightRequests; + setTimeout(this._done(), WAITING_TIME); } else { - setTimeout(this._quit(), 100); + setTimeout(this._quit(), WAITING_TIME); } }, @@ -438,7 +432,7 @@ var Driver = (function DriverClosure() { id: task.id, numPages: task.pdfDoc ? (task.lastPage || task.pdfDoc.numPages) : 0, - lastPageNum: this._getLastPageNum(task), + lastPageNum: this._getLastPageNumber(task), failure: failure, file: task.file, round: task.round, @@ -454,7 +448,7 @@ var Driver = (function DriverClosure() { var r = new XMLHttpRequest(); r.open('POST', url, true); r.setRequestHeader('Content-Type', 'application/json'); - r.onreadystatechange = function sendResultOnreadystatechange(e) { + r.onreadystatechange = function(e) { if (r.readyState === 4) { self.inFlightRequests--; @@ -469,7 +463,7 @@ var Driver = (function DriverClosure() { } } }; - this.inflight.innerHTML = this.inFlightRequests++; + this.inflight.textContent = this.inFlightRequests++; r.send(message); } }; From e02ab9fb79d0c51e46f85279bd2a11cfb2f042d3 Mon Sep 17 00:00:00 2001 From: Tim van der Meij Date: Fri, 15 May 2015 19:58:34 +0200 Subject: [PATCH 5/5] Implement an option to disable automatic scrolling This adds a checkbox with which one can disable scrolling, for example to look back at output during testing. Note that the styles are inline because the test runner removes all