From a470b836d5d27e0e426beea0afdcde0e14f1af5a Mon Sep 17 00:00:00 2001 From: Jerome Wu Date: Wed, 2 Oct 2019 16:53:25 +0800 Subject: [PATCH] Update tests --- package.json | 3 +- scripts/server.js | 2 +- scripts/test-helper.js | 5 + src/createJob.js | 18 +- src/createScheduler.js | 37 +++- src/createWorker.js | 74 +++++-- src/utils/getId.js | 3 + src/worker-script/index.js | 13 +- src/worker/browser/defaultOptions.js | 4 +- src/worker/browser/defaultOptions.js~ | 18 ++ src/worker/browser/loadImage.js | 4 +- tests/constants.js | 33 +++ tests/detect.test.html | 3 +- tests/detect.test.js | 46 ++-- tests/recognize.test.html | 3 +- tests/recognize.test.js | 296 +++++++++----------------- tests/scheduler.test.html | 18 ++ tests/scheduler.test.js | 35 +++ 18 files changed, 341 insertions(+), 274 deletions(-) create mode 100644 src/utils/getId.js create mode 100644 src/worker/browser/defaultOptions.js~ create mode 100644 tests/constants.js create mode 100644 tests/scheduler.test.html create mode 100644 tests/scheduler.test.js diff --git a/package.json b/package.json index 6d93d3c..2a7c124 100644 --- a/package.json +++ b/package.json @@ -10,13 +10,14 @@ "start": "node scripts/server.js", "build": "rimraf dist && webpack --config scripts/webpack.config.prod.js", "prepublishOnly": "npm run build", - "wait": "wait-on http://localhost:3000/package.json", + "wait": "rimraf dist && wait-on http://localhost:3000/dist/tesseract.dev.js", "test": "npm-run-all -p -r start test:all", "test:all": "npm-run-all wait test:browser:* test:node", "test:node": "nyc mocha --exit --bail --require ./scripts/test-helper.js ./tests/*.test.js", "test:browser-tpl": "mocha-headless-chrome -a incognito -a no-sandbox -a disable-setuid-sandbox -t 300000", "test:browser:detect": "npm run test:browser-tpl -- -f ./tests/detect.test.html", "test:browser:recognize": "npm run test:browser-tpl -- -f ./tests/recognize.test.html", + "test:browser:scheduler": "npm run test:browser-tpl -- -f ./tests/scheduler.test.html", "lint": "eslint src", "postinstall": "opencollective-postinstall || true" }, diff --git a/scripts/server.js b/scripts/server.js index a9c5b02..4ccc4b5 100644 --- a/scripts/server.js +++ b/scripts/server.js @@ -10,7 +10,7 @@ const app = express(); app.use(cors()); app.use('/', express.static(path.resolve(__dirname, '..'))); -app.use(middleware(compiler, { publicPath: '/dist' })); +app.use(middleware(compiler, { publicPath: '/dist', writeToDisk: true })); module.exports = app.listen(3000, () => { console.log('Server is running on port 3000'); diff --git a/scripts/test-helper.js b/scripts/test-helper.js index f164d7d..027b300 100644 --- a/scripts/test-helper.js +++ b/scripts/test-helper.js @@ -1,4 +1,9 @@ +const constants = require('../tests/constants'); global.expect = require('expect.js'); global.fs = require('fs'); global.path = require('path'); global.Tesseract = require('../src'); + +Object.keys(constants).forEach((key) => { + global[key] = constants[key]; +}); diff --git a/src/createJob.js b/src/createJob.js index 258c6dc..f90364d 100644 --- a/src/createJob.js +++ b/src/createJob.js @@ -1,11 +1,17 @@ -let jobCounter = 1; +const getId = require('./utils/getId'); -module.exports = ( +let jobCounter = 0; + +module.exports = ({ + id: _id, action, - payload, -) => { - const id = `Job-${jobCounter}-${Math.random().toString(16).slice(3, 8)}`; - jobCounter += 1; + payload = {}, +}) => { + let id = _id; + if (typeof id === 'undefined') { + id = getId('Job', jobCounter); + jobCounter += 1; + } return { id, diff --git a/src/createScheduler.js b/src/createScheduler.js index dcc1569..c2003f4 100644 --- a/src/createScheduler.js +++ b/src/createScheduler.js @@ -1,8 +1,20 @@ +const createJob = require('./createJob'); +const log = require('./utils/log'); +const getId = require('./utils/getId'); + +let schedulerCounter = 0; + module.exports = () => { + const id = getId('Scheduler', schedulerCounter); const workers = {}; const runningWorkers = {}; let jobQueue = []; + schedulerCounter += 1; + + const getQueueLen = () => jobQueue.length; + const getNumWorkers = () => Object.keys(workers).length; + const dequeue = () => { if (jobQueue.length !== 0) { const wIds = Object.keys(workers); @@ -17,11 +29,12 @@ module.exports = () => { const queue = (action, payload) => ( new Promise((resolve, reject) => { + const job = createJob({ action, payload }); jobQueue.push(async (w) => { jobQueue.shift(); - runningWorkers[w.id] = true; + runningWorkers[w.id] = job; try { - resolve(await w[action].apply(this, payload)); + resolve(await w[action].apply(this, [...payload, job.id])); } catch (err) { reject(err); } finally { @@ -29,22 +42,30 @@ module.exports = () => { dequeue(); } }); + log(`[${id}]: add ${job.id} to JobQueue`); + log(`[${id}]: JobQueue length=${jobQueue.length}`); dequeue(); }) ); const addWorker = (w) => { workers[w.id] = w; + log(`[${id}]: add ${w.id}`); + log(`[${id}]: number of workers=${getNumWorkers()}`); + dequeue(); return w.id; }; - const addJob = (action, ...payload) => ( - queue(action, payload) - ); + const addJob = async (action, ...payload) => { + if (getNumWorkers() === 0) { + throw Error(`[${id}]: You need to have at least one worker before adding jobs`); + } + return queue(action, payload); + }; const terminate = async () => { - Object.keys(workers).forEach(async (id) => { - await workers[id].terminate(); + Object.keys(workers).forEach(async (wid) => { + await workers[wid].terminate(); }); jobQueue = []; }; @@ -53,5 +74,7 @@ module.exports = () => { addWorker, addJob, terminate, + getQueueLen, + getNumWorkers, }; }; diff --git a/src/createWorker.js b/src/createWorker.js index 8f16a42..0689757 100644 --- a/src/createWorker.js +++ b/src/createWorker.js @@ -2,6 +2,7 @@ const resolvePaths = require('./utils/resolvePaths'); const circularize = require('./utils/circularize'); const createJob = require('./createJob'); const log = require('./utils/log'); +const getId = require('./utils/getId'); const { defaultOEM } = require('./constants/config'); const { defaultOptions, @@ -12,11 +13,10 @@ const { send, } = require('./worker/node'); -let workerCounter = 1; +let workerCounter = 0; module.exports = (_options = {}) => { - const id = `Worker-${workerCounter}-${Math.random().toString(16).slice(3, 8)}`; - workerCounter += 1; + const id = getId('Worker', workerCounter); const { logger, ...options @@ -28,6 +28,8 @@ module.exports = (_options = {}) => { const rejects = {}; let worker = spawnWorker(options); + workerCounter += 1; + const setResolve = (action, res) => { resolves[action] = res; }; @@ -36,10 +38,9 @@ module.exports = (_options = {}) => { rejects[action] = rej; }; - const startJob = (action, payload = {}) => ( + const startJob = ({ id: jobId, action, payload }) => ( new Promise((resolve, reject) => { - const { id: jobId } = createJob(action, payload); - log(`[${id}]: Start ${jobId}, action=${action}`); + log(`[${id}]: Start ${jobId}, action=${action}, payload=`, payload); setResolve(action, resolve); setReject(action, reject); send(worker, { @@ -51,32 +52,58 @@ module.exports = (_options = {}) => { }) ); - const load = () => ( - startJob('load', { options }) + const load = jobId => ( + startJob(createJob({ + id: jobId, action: 'load', payload: { options }, + })) ); - const loadLanguage = (langs = 'eng') => ( - startJob('loadLanguage', { langs, options }) + const loadLanguage = (langs = 'eng', jobId) => ( + startJob(createJob({ + id: jobId, + action: 'loadLanguage', + payload: { langs, options }, + })) ); - const initialize = (langs = 'eng', oem = defaultOEM) => ( - startJob('initialize', { langs, oem }) + const initialize = (langs = 'eng', oem = defaultOEM, jobId) => ( + startJob(createJob({ + id: jobId, + action: 'initialize', + payload: { langs, oem }, + })) ); - const setParameters = (params = {}) => ( - startJob('setParameters', { params }) + const setParameters = (params = {}, jobId) => ( + startJob(createJob({ + id: jobId, + action: 'setParameters', + payload: { params }, + })) ); - const recognize = async (image, opts = {}) => ( - startJob('recognize', { image: await loadImage(image), options: opts }) + const recognize = async (image, opts = {}, jobId) => ( + startJob(createJob({ + id: jobId, + action: 'recognize', + payload: { image: await loadImage(image), options: opts }, + })) ); - const getPDF = (title = 'Tesseract OCR Result', textonly = false) => ( - startJob('getPDF', { title, textonly }) + const getPDF = (title = 'Tesseract OCR Result', textonly = false, jobId) => ( + startJob(createJob({ + id: jobId, + action: 'getPDF', + payload: { title, textonly }, + })) ); - const detect = async image => ( - startJob('detect', { image: await loadImage(image) }) + const detect = async (image, jobId) => ( + startJob(createJob({ + id: jobId, + action: 'detect', + payload: { image: await loadImage(image) }, + })) ); const terminate = async () => { @@ -88,15 +115,18 @@ module.exports = (_options = {}) => { return Promise.resolve(); }; - onMessage(worker, ({ status, action, data }) => { + onMessage(worker, ({ + workerId, jobId, status, action, data, + }) => { if (status === 'resolve') { + log(`[${workerId}]: Complete ${jobId}, data=`, data); let d = data; if (action === 'recognize') { d = circularize(data); } else if (action === 'getPDF') { d = Array.from({ ...data, length: Object.keys(data).length }); } - resolves[action](d); + resolves[action]({ jobId, data: d }); } else if (status === 'reject') { rejects[action](data); throw Error(data); diff --git a/src/utils/getId.js b/src/utils/getId.js new file mode 100644 index 0000000..8c2b119 --- /dev/null +++ b/src/utils/getId.js @@ -0,0 +1,3 @@ +module.exports = (prefix, cnt) => ( + `${prefix}-${cnt}-${Math.random().toString(16).slice(3, 8)}` +); diff --git a/src/worker-script/index.js b/src/worker-script/index.js index b72a28b..621638b 100644 --- a/src/worker-script/index.js +++ b/src/worker-script/index.js @@ -15,6 +15,7 @@ const dump = require('./utils/dump'); const isBrowser = require('../utils/getEnvironment')('type') === 'browser'; const setImage = require('./utils/setImage'); const defaultParams = require('./constants/defaultParams'); +const log = require('../utils/log'); /* * Tesseract Module returned by TesseractCore. @@ -23,7 +24,7 @@ let TessModule; /* * TessearctBaseAPI instance */ -let api; +let api = null; let latestJob; let adapter = {}; let params = defaultParams; @@ -77,11 +78,13 @@ const loadLanguage = async ({ try { const _data = await readCache(`${cachePath || '.'}/${lang}.traineddata`); if (typeof _data !== 'undefined') { + log(`[${workerId}]: Load ${lang}.traineddata from cache`); data = _data; } else { throw Error('Not found in cache'); } } catch (e) { + log(`[${workerId}]: Load ${lang}.traineddata from ${langPath}`); if (typeof _lang === 'string') { let path = null; @@ -173,8 +176,12 @@ const initialize = ({ res.progress({ workerId, status: 'initializing api', progress: 0, }); + if (api !== null) { + api.End(); + } api = new TessModule.TessBaseAPI(); api.Init(null, langs, oem); + params = defaultParams; setParameters({ payload: { params } }); res.progress({ workerId, status: 'initialized api', progress: 1, @@ -242,7 +249,9 @@ const detect = ({ payload: { image } }, res) => { const terminate = (_, res) => { try { - api.End(); + if (api !== null) { + api.End(); + } res.resolve({ terminated: true }); } catch (err) { res.reject(err.toString()); diff --git a/src/worker/browser/defaultOptions.js b/src/worker/browser/defaultOptions.js index 64d0851..cef5e58 100644 --- a/src/worker/browser/defaultOptions.js +++ b/src/worker/browser/defaultOptions.js @@ -1,5 +1,5 @@ const resolveURL = require('resolve-url'); -const { version } = require('../../../package.json'); +const { version, dependencies } = require('../../../package.json'); const defaultOptions = require('../../constants/defaultOptions'); /* @@ -14,5 +14,5 @@ module.exports = { * If browser doesn't support WebAssembly, * load ASM version instead */ - corePath: `https://unpkg.com/tesseract.js-core@v2.0.0-beta.10/tesseract-core.${typeof WebAssembly === 'object' ? 'wasm' : 'asm'}.js`, + corePath: `https://unpkg.com/tesseract.js-core@v${dependencies['tesseract.js-core'].substring(1)}/tesseract-core.${typeof WebAssembly === 'object' ? 'wasm' : 'asm'}.js`, }; diff --git a/src/worker/browser/defaultOptions.js~ b/src/worker/browser/defaultOptions.js~ new file mode 100644 index 0000000..cef5e58 --- /dev/null +++ b/src/worker/browser/defaultOptions.js~ @@ -0,0 +1,18 @@ +const resolveURL = require('resolve-url'); +const { version, dependencies } = require('../../../package.json'); +const defaultOptions = require('../../constants/defaultOptions'); + +/* + * Default options for browser worker + */ +module.exports = { + ...defaultOptions, + workerPath: (typeof process !== 'undefined' && process.env.TESS_ENV === 'development') + ? resolveURL(`/dist/worker.dev.js?nocache=${Math.random().toString(36).slice(3)}`) + : `https://unpkg.com/tesseract.js@v${version}/dist/worker.min.js`, + /* + * If browser doesn't support WebAssembly, + * load ASM version instead + */ + corePath: `https://unpkg.com/tesseract.js-core@v${dependencies['tesseract.js-core'].substring(1)}/tesseract-core.${typeof WebAssembly === 'object' ? 'wasm' : 'asm'}.js`, +}; diff --git a/src/worker/browser/loadImage.js b/src/worker/browser/loadImage.js index a57b4f4..b849153 100644 --- a/src/worker/browser/loadImage.js +++ b/src/worker/browser/loadImage.js @@ -56,10 +56,10 @@ const loadImage = async (image) => { } } else if (image instanceof HTMLElement) { if (image.tagName === 'IMG') { - data = loadImage(image.src); + data = await loadImage(image.src); } if (image.tagName === 'VIDEO') { - data = loadImage(image.poster); + data = await loadImage(image.poster); } if (image.tagName === 'CANVAS') { await new Promise((resolve) => { diff --git a/tests/constants.js b/tests/constants.js new file mode 100644 index 0000000..e65f493 --- /dev/null +++ b/tests/constants.js @@ -0,0 +1,33 @@ +const TIMEOUT = 10000; +const IMAGE_PATH = 'http://localhost:3000/tests/assets/images'; +const IS_BROWSER = typeof window !== 'undefined' && typeof window.document !== 'undefined'; +const OPTIONS = { + langPath: 'http://localhost:3000/tests/assets/traineddata', + cachePath: './tests/assets/traineddata', + ...(IS_BROWSER ? { workerPath: '/dist/worker.dev.js' } : {}), +}; +const SIMPLE_TEXT = 'Tesseract.js\n'; +const SIMPLE_TEXT_HALF = 'Tesse\n'; +const COMSIC_TEXT = 'HellO World\nfrom beyond\nthe Cosmic Void\n'; +const TESTOCR_TEXT = 'This is a lot of 12 point text to test the\nocr code and see if it works on all types\nof file format.\n\nThe quick brown dog jumped over the\nlazy fox. The quick brown dog jumped\nover the lazy fox. The quick brown dog\njumped over the lazy fox. The quick\nbrown dog jumped over the lazy fox.\n'; +const CHINESE_TEXT = '繁 體 中 文 測 試\n'; +const FORMATS = ['png', 'jpg', 'bmp', 'pbm']; +const SIMPLE_PNG_BASE64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAAC0CAIAAABqhmJGAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAASuSURBVHhe7dftVdswAIbRzsVAzMM0XabDUCOUxLYsWW4Jp+/pvf9w9GH76CHw4x2IJWAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAI9p8G/PbyY8rL2686g8t+vnqHTyfgIYfvz/26veTXn/UKX8+f0EU9bHrtu/6KfAN/AwEXAj7lFf2TBFw4nae8on+SgIvJ01n/KLzpDK+L3bT/Ap4O+HC+V12mTH+M3gzcLbIY/EO6HfxYp13k09nb6r3UqcdnjoCL3ll72J26h+35Oxy2XvZ0wOLaXq9v2+F1UC+7RZtMZ/DnfX1lwDOPzwUCLo7O2trtDK8H3M/iqoc6bj1subT68XTA/F7bGJooyzKbhTvLPHY8eJLHlbNX1DqYUVfdXbqwJjsCLsans37aNNJM6w68OR0wv9f9ymKw3k67yn2ZZpHlg3a3zis60s6oV+ZvlzMCLoanc3Dsdt9TdWT/lM8OmNjr5KY72jmzq1zfrbvXtVtmRMDF8HTWcgaaqIrD1U4G/MFewxrW262s5jS/Fzpmdts6mnHy+Fwl4GJ0OjsNrG1P/y7CNo3+gEt7jW56MVprNed7A/5w+n6YJ+BieDpnj/jO6pweTz0acGWvmZveL9XOmd3x6wKuTt8PEwRczLRw4eje1XX7c/cDruw1uuneOu2c4aOvzI57mJhRh1xZlQ0BF+Oz9vcF96fuB1zYa7R2b5mD6/XSwdfg8snj4q21+W/L02dfzIxhQMDFyTm6Hd7m+JYP7rPKT5sRuzhOBywm91rUkYc3fV9ltchtr8VmzuGOdfDB9N1tFYefNfdXLmyGjNZkhoCLUQufVqd/7z7rUcLW/XieDvg0s9difNOdRV5ePibt5vTuazusWbF9rs2E5v4mH58LBFyMW7g5OID7s9cMuTygmt9rcNPb5MrAz0lHc3Z9Ht7XZsxqxO36ZtLR/c0+PpMEzLOc/4LhrwmYZ6lfywJ+JgHzJPr9DgLmi23/zdXvcwmYL7YKWL1PJ2AIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmAIJmCI9f7+G6yFxVg/GyYwAAAAAElFTkSuQmCC'; +const SIMPLE_JPG_BASE64 = 'data:image/jpeg;base64,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'; + +if (typeof module !== 'undefined') { + module.exports = { + TIMEOUT, + IMAGE_PATH, + IS_BROWSER, + SIMPLE_PNG_BASE64, + SIMPLE_JPG_BASE64, + CHINESE_TEXT, + SIMPLE_TEXT, + SIMPLE_TEXT_HALF, + COMSIC_TEXT, + TESTOCR_TEXT, + FORMATS, + OPTIONS, + }; +} diff --git a/tests/detect.test.html b/tests/detect.test.html index c119a6b..0206775 100644 --- a/tests/detect.test.html +++ b/tests/detect.test.html @@ -7,7 +7,8 @@
- + + - + + + + + + + + + + diff --git a/tests/scheduler.test.js b/tests/scheduler.test.js new file mode 100644 index 0000000..1bab4ca --- /dev/null +++ b/tests/scheduler.test.js @@ -0,0 +1,35 @@ +const { createScheduler, createWorker } = Tesseract; + +let workers = []; + +before(async function cb() { + this.timeout(0); + const NUM_WORKERS = 10; + console.log(`Initializing ${NUM_WORKERS} workers`); + workers = await Promise.all(Array(NUM_WORKERS).fill(0).map(async () => { + const w = createWorker(OPTIONS); + await w.load(); + await w.loadLanguage('eng'); + await w.initialize('eng'); + return w; + })); + console.log(`Initialized ${NUM_WORKERS} workers`); +}); + +describe('scheduler', () => { + describe('should speed up with more workers (running 20 jobs)', () => { + Array(10).fill(0).forEach((_, num) => ( + it(`support using ${num + 1} workers`, async () => { + const NUM_JOBS = 30; + const scheduler = createScheduler(); + workers.slice(0, num + 1).forEach((w) => { + scheduler.addWorker(w); + }); + const rets = await Promise.all(Array(NUM_JOBS).fill(0).map((_, idx) => ( + scheduler.addJob('recognize', `${IMAGE_PATH}/${idx % 2 === 0 ? 'simple' : 'cosmic'}.png`) + ))); + expect(rets.length).to.be(NUM_JOBS); + }).timeout(60000) + )); + }); +});