diff options
| -rw-r--r-- | test/dictionary-validate.js | 8 | ||||
| -rw-r--r-- | test/schema-validate.js | 6 | ||||
| -rw-r--r-- | test/test-database.js | 42 | ||||
| -rw-r--r-- | test/test-document.js | 28 | ||||
| -rw-r--r-- | test/test-schema.js | 12 | ||||
| -rw-r--r-- | test/yomichan-test.js | 13 | ||||
| -rw-r--r-- | test/yomichan-vm.js | 174 | 
7 files changed, 225 insertions, 58 deletions
| diff --git a/test/dictionary-validate.js b/test/dictionary-validate.js index 14eee2ed..6496f2ac 100644 --- a/test/dictionary-validate.js +++ b/test/dictionary-validate.js @@ -18,10 +18,12 @@  const fs = require('fs');  const path = require('path'); -const yomichanTest = require('./yomichan-test'); +const {JSZip} = require('./yomichan-test'); +const {VM} = require('./yomichan-vm'); -const JSZip = yomichanTest.JSZip; -const {JsonSchema} = yomichanTest.requireScript('ext/bg/js/json-schema.js', ['JsonSchema']); +const vm = new VM(); +vm.execute('bg/js/json-schema.js'); +const JsonSchema = vm.get('JsonSchema');  function readSchema(relativeFileName) { diff --git a/test/schema-validate.js b/test/schema-validate.js index a4f2d94c..eb31aa8d 100644 --- a/test/schema-validate.js +++ b/test/schema-validate.js @@ -17,9 +17,11 @@   */  const fs = require('fs'); -const yomichanTest = require('./yomichan-test'); +const {VM} = require('./yomichan-vm'); -const {JsonSchema} = yomichanTest.requireScript('ext/bg/js/json-schema.js', ['JsonSchema']); +const vm = new VM(); +vm.execute('bg/js/json-schema.js'); +const JsonSchema = vm.get('JsonSchema');  function main() { diff --git a/test/test-database.js b/test/test-database.js index 35f22523..fcac9a4d 100644 --- a/test/test-database.js +++ b/test/test-database.js @@ -21,6 +21,7 @@ const url = require('url');  const path = require('path');  const assert = require('assert');  const yomichanTest = require('./yomichan-test'); +const {VM} = require('./yomichan-vm');  require('fake-indexeddb/auto');  const chrome = { @@ -88,23 +89,24 @@ class XMLHttpRequest {      }  } -const {JsonSchema} = yomichanTest.requireScript('ext/bg/js/json-schema.js', ['JsonSchema']); -const {dictFieldSplit, dictTagSanitize} = yomichanTest.requireScript('ext/bg/js/dictionary.js', ['dictFieldSplit', 'dictTagSanitize']); -const {stringReverse} = yomichanTest.requireScript('ext/mixed/js/core.js', ['stringReverse'], {chrome}); -const {requestJson} = yomichanTest.requireScript('ext/bg/js/request.js', ['requestJson'], {XMLHttpRequest}); -const databaseGlobals = { +const vm = new VM({      chrome, -    JsonSchema, -    requestJson, -    stringReverse, -    dictFieldSplit, -    dictTagSanitize, +    XMLHttpRequest,      indexedDB: global.indexedDB, +    IDBKeyRange: global.IDBKeyRange,      JSZip: yomichanTest.JSZip -}; -databaseGlobals.window = databaseGlobals; -const {Database} = yomichanTest.requireScript('ext/bg/js/database.js', ['Database'], databaseGlobals); +}); +vm.context.window = vm.context; + +vm.execute([ +    'bg/js/json-schema.js', +    'bg/js/dictionary.js', +    'mixed/js/core.js', +    'bg/js/request.js', +    'bg/js/database.js' +]); +const Database = vm.get('Database');  function countTermsWithExpression(terms, expression) { @@ -212,20 +214,20 @@ async function testDatabase1() {              },              {prefixWildcardsSupported: true}          ); -        assert.deepStrictEqual(errors, []); -        assert.deepStrictEqual(result, expectedSummary); +        vm.assert.deepStrictEqual(errors, []); +        vm.assert.deepStrictEqual(result, expectedSummary);          assert.ok(progressEvent);          // Get info summary          const info = await database.getDictionaryInfo(); -        assert.deepStrictEqual(info, [expectedSummary]); +        vm.assert.deepStrictEqual(info, [expectedSummary]);          // Get counts          const counts = await database.getDictionaryCounts(              info.map((v) => v.title),              true          ); -        assert.deepStrictEqual(counts, { +        vm.assert.deepStrictEqual(counts, {              counts: [{kanji: 2, kanjiMeta: 2, terms: 32, termMeta: 3, tagMeta: 12}],              total: {kanji: 2, kanjiMeta: 2, terms: 32, termMeta: 3, tagMeta: 12}          }); @@ -248,10 +250,10 @@ async function testDatabase1() {  async function testDatabaseEmpty1(database) {      const info = await database.getDictionaryInfo(); -    assert.deepStrictEqual(info, []); +    vm.assert.deepStrictEqual(info, []);      const counts = await database.getDictionaryCounts([], true); -    assert.deepStrictEqual(counts, { +    vm.assert.deepStrictEqual(counts, {          counts: [],          total: {kanji: 0, kanjiMeta: 0, terms: 0, termMeta: 0, tagMeta: 0}      }); @@ -824,7 +826,7 @@ async function testFindTagForTitle1(database, title) {      for (const {inputs, expectedResults} of data) {          for (const {name} of inputs) {              const result = await database.findTagForTitle(name, title); -            assert.deepStrictEqual(result, expectedResults.value); +            vm.assert.deepStrictEqual(result, expectedResults.value);          }      }  } diff --git a/test/test-document.js b/test/test-document.js index edf6bbea..ab5f5716 100644 --- a/test/test-document.js +++ b/test/test-document.js @@ -2,7 +2,7 @@ const fs = require('fs');  const path = require('path');  const assert = require('assert');  const {JSDOM} = require('jsdom'); -const yomichanTest = require('./yomichan-test'); +const {VM} = require('./yomichan-vm');  // DOMRect class definition @@ -74,20 +74,18 @@ async function testDocument1() {      const Node = window.Node;      const Range = window.Range; -    const {DOM} = yomichanTest.requireScript( -        'ext/mixed/js/dom.js', -        ['DOM'] -    ); -    const {TextSourceRange, TextSourceElement} = yomichanTest.requireScript( -        'ext/fg/js/source.js', -        ['TextSourceRange', 'TextSourceElement'], -        {document, window, Range, Node} -    ); -    const {docRangeFromPoint, docSentenceExtract} = yomichanTest.requireScript( -        'ext/fg/js/document.js', -        ['docRangeFromPoint', 'docSentenceExtract'], -        {document, window, Node, TextSourceElement, TextSourceRange, DOM} -    ); +    const vm = new VM({document, window, Range, Node}); +    vm.execute([ +        'mixed/js/dom.js', +        'fg/js/source.js', +        'fg/js/document.js' +    ]); +    const [TextSourceRange, TextSourceElement, docRangeFromPoint, docSentenceExtract] = vm.get([ +        'TextSourceRange', +        'TextSourceElement', +        'docRangeFromPoint', +        'docSentenceExtract' +    ]);      try {          await testDocumentTextScanningFunctions(dom, {docRangeFromPoint, docSentenceExtract, TextSourceRange, TextSourceElement}); diff --git a/test/test-schema.js b/test/test-schema.js index f4612f86..5f9915fd 100644 --- a/test/test-schema.js +++ b/test/test-schema.js @@ -17,9 +17,11 @@   */  const assert = require('assert'); -const yomichanTest = require('./yomichan-test'); +const {VM} = require('./yomichan-vm'); -const {JsonSchema} = yomichanTest.requireScript('ext/bg/js/json-schema.js', ['JsonSchema']); +const vm = new VM(); +vm.execute('bg/js/json-schema.js'); +const JsonSchema = vm.get('JsonSchema');  function testValidate1() { @@ -138,7 +140,7 @@ function testGetValidValueOrDefault1() {      for (const [value, expected] of testData) {          const actual = JsonSchema.getValidValueOrDefault(schema, value); -        assert.deepStrictEqual(actual, expected); +        vm.assert.deepStrictEqual(actual, expected);      }  } @@ -177,7 +179,7 @@ function testGetValidValueOrDefault2() {      for (const [value, expected] of testData) {          const actual = JsonSchema.getValidValueOrDefault(schema, value); -        assert.deepStrictEqual(actual, expected); +        vm.assert.deepStrictEqual(actual, expected);      }  } @@ -235,7 +237,7 @@ function testGetValidValueOrDefault3() {      for (const [value, expected] of testData) {          const actual = JsonSchema.getValidValueOrDefault(schema, value); -        assert.deepStrictEqual(actual, expected); +        vm.assert.deepStrictEqual(actual, expected);      }  } diff --git a/test/yomichan-test.js b/test/yomichan-test.js index 78bfb9c6..0fc97b4b 100644 --- a/test/yomichan-test.js +++ b/test/yomichan-test.js @@ -22,18 +22,6 @@ const path = require('path');  let JSZip = null; -function requireScript(fileName, exportNames, variables) { -    const absoluteFileName = path.join(__dirname, '..', fileName); -    const source = fs.readFileSync(absoluteFileName, {encoding: 'utf8'}); -    const exportNamesString = Array.isArray(exportNames) ? exportNames.join(',') : ''; -    const variablesArgumentName = '__variables__'; -    let variableString = ''; -    if (typeof variables === 'object' && variables !== null) { -        variableString = Object.keys(variables).join(','); -        variableString = `const {${variableString}} = ${variablesArgumentName};`; -    } -    return Function(variablesArgumentName, `'use strict';${variableString}${source}\n;return {${exportNamesString}};`)(variables); -}  function getJSZip() {      if (JSZip === null) { @@ -64,7 +52,6 @@ function createTestDictionaryArchive(dictionary, dictionaryName) {  module.exports = { -    requireScript,      createTestDictionaryArchive,      get JSZip() { return getJSZip(); }  }; diff --git a/test/yomichan-vm.js b/test/yomichan-vm.js new file mode 100644 index 00000000..ff478844 --- /dev/null +++ b/test/yomichan-vm.js @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2020  Alex Yatskov <alex@foosoft.net> + * Author: Alex Yatskov <alex@foosoft.net> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <https://www.gnu.org/licenses/>. + */ + +const fs = require('fs'); +const vm = require('vm'); +const path = require('path'); +const assert = require('assert'); + + +function getContextEnvironmentRecords(context, names) { +    // Enables export of values from the declarative environment record +    if (!Array.isArray(names) || names.length === 0) { +        return []; +    } + +    let scriptSource = '(() => {\n    "use strict";\n    const results = [];'; +    for (const name of names) { +        scriptSource += `\n    try { results.push(${name}); } catch (e) { results.push(void 0); }`; +    } +    scriptSource += '\n    return results;\n})();'; + +    const script = new vm.Script(scriptSource, {filename: 'getContextEnvironmentRecords'}); + +    const contextHasNames = Object.prototype.hasOwnProperty.call(context, 'names'); +    const contextNames = context.names; +    context.names = names; + +    const results = script.runInContext(context, {}); + +    if (contextHasNames) { +        context.names = contextNames; +    } else { +        delete context.names; +    } + +    return Array.from(results); +} + +function isDeepStrictEqual(val1, val2) { +    if (val1 === val2) { return true; } + +    if (Array.isArray(val1)) { +        if (Array.isArray(val2)) { +            return isArrayDeepStrictEqual(val1, val2); +        } +    } else if (typeof val1 === 'object' && val1 !== null) { +        if (typeof val2 === 'object' && val2 !== null) { +            return isObjectDeepStrictEqual(val1, val2); +        } +    } + +    return false; +} + +function isArrayDeepStrictEqual(val1, val2) { +    const ii = val1.length; +    if (ii !== val2.length) { return false; } + +    for (let i = 0; i < ii; ++i) { +        if (!isDeepStrictEqual(val1[i], val2[i])) { +            return false; +        } +    } + +    return true; +} + +function isObjectDeepStrictEqual(val1, val2) { +    const keys1 = Object.keys(val1); +    const keys2 = Object.keys(val2); + +    if (keys1.length !== keys2.length) { return false; } + +    const keySet = new Set(keys1); +    for (const key of keys2) { +        if (!keySet.delete(key)) { return false; } +    } + +    for (const key of keys1) { +        if (!isDeepStrictEqual(val1[key], val2[key])) { +            return false; +        } +    } + +    const tag1 = Object.prototype.toString.call(val1); +    const tag2 = Object.prototype.toString.call(val2); +    if (tag1 !== tag2) { return false; } + +    return true; +} + +function deepStrictEqual(actual, expected) { +    try { +        // This will fail on prototype === comparison on cross context objects +        assert.deepStrictEqual(actual, expected); +    } catch (e) { +        if (!isDeepStrictEqual(actual, expected)) { +            throw e; +        } +    } +} + + +class VM { +    constructor(context={}) { +        this._context = vm.createContext(context); +        this._assert = { +            deepStrictEqual +        }; +    } + +    get context() { +        return this._context; +    } + +    get assert() { +        return this._assert; +    } + +    get(names) { +        if (typeof names === 'string') { +            return getContextEnvironmentRecords(this._context, [names])[0]; +        } else if (Array.isArray(names)) { +            return getContextEnvironmentRecords(this._context, names); +        } else { +            throw new Error('Invalid argument'); +        } +    } + +    set(values) { +        if (typeof values === 'object' && values !== null) { +            Object.assign(this._context, values); +        } else { +            throw new Error('Invalid argument'); +        } +    } + +    execute(fileNames) { +        const single = !Array.isArray(fileNames); +        if (single) { +            fileNames = [fileNames]; +        } + +        const results = []; +        for (const fileName of fileNames) { +            const absoluteFileName = path.resolve(__dirname, '..', 'ext', fileName); +            const source = fs.readFileSync(absoluteFileName, {encoding: 'utf8'}); +            const script = new vm.Script(source, {filename: absoluteFileName}); +            results.push(script.runInContext(this._context, {})); +        } + +        return single ? results[0] : results; +    } +} + + +module.exports = { +    VM +}; |