From 9790d0f280c6a61fc6533c15424da2ec9a6c8c3e Mon Sep 17 00:00:00 2001 From: RunDevelopment Date: Tue, 7 Jan 2020 21:55:59 +0100 Subject: [PATCH] Tests are now faster --- tests/checks/extend.js | 20 ------ tests/checks/insert-before.js | 32 --------- tests/helper/check-functionality.js | 13 ---- tests/helper/checks.js | 63 ++++++++++++++++ tests/helper/prism-loader.js | 108 ++++++++++------------------ 5 files changed, 102 insertions(+), 134 deletions(-) delete mode 100644 tests/checks/extend.js delete mode 100644 tests/checks/insert-before.js delete mode 100644 tests/helper/check-functionality.js create mode 100644 tests/helper/checks.js diff --git a/tests/checks/extend.js b/tests/checks/extend.js deleted file mode 100644 index b699dd374d..0000000000 --- a/tests/checks/extend.js +++ /dev/null @@ -1,20 +0,0 @@ -const { testFunction } = require('./../helper/check-functionality'); - -function extendTest(id, redef) { - const details = `\nextend("${id}", ${redef})`; - - // type checks - if (typeof id !== 'string') { - throw new TypeError(`The id argument has to be a 'string'.` + details); - } - if (typeof redef !== 'object') { - throw new TypeError(`The redef argument has to be an 'object'.` + details); - } - - - if (!(id in Prism.languages)) { - throw new Error(`Cannot extend '${id}' because it is not defined in Prism.languages.`); - } -} - -testFunction('extend', Prism.languages, extendTest); diff --git a/tests/checks/insert-before.js b/tests/checks/insert-before.js deleted file mode 100644 index 85363455b5..0000000000 --- a/tests/checks/insert-before.js +++ /dev/null @@ -1,32 +0,0 @@ -const { testFunction } = require('./../helper/check-functionality'); - -function insertBeforeTest(inside, before, insert, root) { - const details = `\ninsertBefore("${inside}", "${before}", ${insert}, ${root})`; - - // type checks - if (typeof inside !== 'string') { - throw new TypeError(`The inside argument has to be a 'string'.` + details); - } - if (typeof before !== 'string') { - throw new TypeError(`The before argument has to be a 'string'.` + details); - } - if (typeof insert !== 'object') { - throw new TypeError(`The insert argument has to be an 'object'.` + details); - } - if (root && typeof root !== 'object') { - throw new TypeError(`The root argument has to be an 'object' if defined.` + details); - } - - - root = root || Prism.languages; - var grammar = root[inside]; - - if (typeof grammar !== 'object') { - throw new Error(`The grammar "${inside}" has to be an 'object' not '${typeof grammar}'.`); - } - if (!(before in grammar)) { - throw new Error(`"${before}" has to be a key of the grammar "${inside}".`); - } -} - -testFunction('insertBefore', Prism.languages, insertBeforeTest); diff --git a/tests/helper/check-functionality.js b/tests/helper/check-functionality.js deleted file mode 100644 index 0192d0cb3c..0000000000 --- a/tests/helper/check-functionality.js +++ /dev/null @@ -1,13 +0,0 @@ -"use strict"; - -module.exports = { - testFunction(name, object, tester) { - const func = object[name]; - - object[name] = function () { - tester.apply(this, arguments); - return func.apply(this, arguments); - }; - } - -} diff --git a/tests/helper/checks.js b/tests/helper/checks.js new file mode 100644 index 0000000000..f926626c34 --- /dev/null +++ b/tests/helper/checks.js @@ -0,0 +1,63 @@ +"use strict"; + +function testFunction(name, object, tester) { + const func = object[name]; + + object[name] = function () { + tester.apply(this, arguments); + return func.apply(this, arguments); + }; +} + +module.exports = (Prism) => { + + function extendTest(id, redef) { + const details = `\nextend("${id}", ${redef})`; + + // type checks + if (typeof id !== 'string') { + throw new TypeError(`The id argument has to be a 'string'.` + details); + } + if (typeof redef !== 'object') { + throw new TypeError(`The redef argument has to be an 'object'.` + details); + } + + + if (!(id in Prism.languages)) { + throw new Error(`Cannot extend '${id}' because it is not defined in Prism.languages.`); + } + } + + function insertBeforeTest(inside, before, insert, root) { + const details = `\ninsertBefore("${inside}", "${before}", ${insert}, ${root})`; + + // type checks + if (typeof inside !== 'string') { + throw new TypeError(`The inside argument has to be a 'string'.` + details); + } + if (typeof before !== 'string') { + throw new TypeError(`The before argument has to be a 'string'.` + details); + } + if (typeof insert !== 'object') { + throw new TypeError(`The insert argument has to be an 'object'.` + details); + } + if (root && typeof root !== 'object') { + throw new TypeError(`The root argument has to be an 'object' if defined.` + details); + } + + + root = root || Prism.languages; + var grammar = root[inside]; + + if (typeof grammar !== 'object') { + throw new Error(`The grammar "${inside}" has to be an 'object' not '${typeof grammar}'.`); + } + if (!(before in grammar)) { + throw new Error(`"${before}" has to be a key of the grammar "${inside}".`); + } + } + + testFunction('extend', Prism.languages, extendTest); + testFunction('insertBefore', Prism.languages, insertBeforeTest); + +}; diff --git a/tests/helper/prism-loader.js b/tests/helper/prism-loader.js index 7bc07487c9..7a4e1f6dd8 100644 --- a/tests/helper/prism-loader.js +++ b/tests/helper/prism-loader.js @@ -1,11 +1,10 @@ "use strict"; const fs = require("fs"); -const vm = require("vm"); -const { getAllFiles } = require("./test-discovery"); const components = require("../../components"); const getLoader = require("../../dependencies"); const languagesCatalog = components.languages; +const coreChecks = require('./checks'); /** @@ -14,6 +13,13 @@ const languagesCatalog = components.languages; * @property {Set} loaded A set of loaded components. */ +/** @type {Map} */ +const fileSourceCache = new Map(); +/** @type {() => any} */ +let coreSupplierFunction = null; +/** @type {Map void>} */ +const languageCache = new Map(); + module.exports = { /** @@ -51,9 +57,15 @@ module.exports = { throw new Error(`Language '${id}' not found.`); } - // load the language itself - const languageSource = this.loadComponentSource(id); - context.Prism = this.runFileWithContext(languageSource, { Prism: context.Prism }).Prism; + // get the function which adds the language from cache + let languageFunction = languageCache.get(id); + if (languageFunction === undefined) { + // make a function from the code which take "Prism" as an argument, so the language grammar + // references the function argument + const func = new Function('Prism', this.loadComponentSource(id)); + languageCache.set(id, languageFunction = (Prism) => func(Prism)); + } + languageFunction(context.Prism); context.loaded.add(id); }); @@ -69,45 +81,26 @@ module.exports = { * @returns {Prism} */ createEmptyPrism() { - const coreSource = this.loadComponentSource("core"); - const context = this.runFileWithContext(coreSource); - - for (const testSource of this.getChecks().map(src => this.loadFileSource(src))) { - context.Prism = this.runFileWithContext(testSource, { - Prism: context.Prism, - /** - * A pseudo require function for the checks. - * - * This function will behave like the regular `require` in real modules when called form a check file. - * - * @param {string} id The id of relative path to require. - */ - require(id) { - if (id.startsWith('./')) { - // We have to rewrite relative paths starting with './' - return require('./../checks/' + id.substr(2)); - } else { - // This might be an id like 'mocha' or 'fs' or a relative path starting with '../'. - // In both cases we don't have to change anything. - return require(id); - } - } - }).Prism; + if (!coreSupplierFunction) { + const source = this.loadComponentSource("core"); + // Core exports itself in 2 ways: + // 1) it uses `module.exports = Prism` which what we'll use + // 2) it uses `global.Prism = Prism` which we want to sabotage to prevent leaking + const func = new Function('module', 'global', source); + coreSupplierFunction = () => { + const module = { + // that's all we care about + exports: {} + }; + func(module, {}); + return module.exports; + }; } - - return context.Prism; + const Prism = coreSupplierFunction(); + coreChecks(Prism); + return Prism; }, - - /** - * Cached file sources, to prevent massive HDD work - * - * @private - * @type {Object.} - */ - fileSourceCache: {}, - - /** * Loads the given component's file source as string * @@ -127,33 +120,10 @@ module.exports = { * @returns {string} */ loadFileSource(src) { - return this.fileSourceCache[src] = this.fileSourceCache[src] || fs.readFileSync(src, "utf8"); - }, - - - checkCache: null, - - /** - * Returns a list of files which add additional checks to Prism functions. - * - * @returns {ReadonlyArray} - */ - getChecks() { - return this.checkCache = this.checkCache || getAllFiles(__dirname + "/../checks"); - }, - - - /** - * Runs a VM for a given file source with the given context - * - * @private - * @param {string} fileSource - * @param {*} [context={}] - * - * @returns {*} - */ - runFileWithContext(fileSource, context = {}) { - vm.runInNewContext(fileSource, context); - return context; + let content = fileSourceCache.get(src); + if (content === undefined) { + fileSourceCache.set(src, content = fs.readFileSync(src, "utf8")); + } + return content; } };