UNPKG

fontoxpath

Version:

A minimalistic XPath 3.1 engine in JavaScript

82 lines (69 loc) 2.73 kB
import { evaluateXPathToNumber, evaluateXPathToBoolean } from 'fontoxpath'; import * as slimdom from 'slimdom'; const cacheId = {}; describe('performance', () => { // Warm up the cache // Counting to 10 milion takes a couple of seconds. before(function () { this.timeout(100000); evaluateXPathToNumber('count(0 to 10000000)', document, null, {}, {cacheId}); }); it('can reuse cached results', function () { evaluateXPathToNumber('count(0 to 10000000)', document, null, {}, {cacheId}); }); it('can reuse cached sub-results', function () { chai.assert.isTrue(evaluateXPathToBoolean('10000001 = count(0 to 10000000)', document, null, {}, {cacheId})); }); }); function timeXPath (xpath, document) { const then = performance.now(); chai.assert(evaluateXPathToBoolean(xpath, document), `The passed XPath ${xpath} should resolve to true`); const now = performance.now(); return now - then; } function fillDocument (document, element, depth) { element.setAttribute('depth', depth); if (depth === 0) { return element; } var prototypeElement = element.appendChild( fillDocument( document, document.createElement('ele'), depth - 1)); for (let i = 1, l = 10; i < l; ++i) { element.appendChild(prototypeElement.cloneNode(true)); } return element; } function runTests (document) { let fullTraversalCost; before(function () { this.timeout(30000); fillDocument(document, document.appendChild(document.createElement('root')), 5); fullTraversalCost = timeXPath('/descendant::element() => count() > 10', document); }); it('Makes queries exit early by streaming them and only consuming the first item', function () { this.timeout(10000); chai.assert.isAtMost( timeXPath('(/descendant::element()["4" = @depth]) => head() => count() = 1', document), fullTraversalCost * 0.5, 'Revaluating a filtered xpath must not cost significantly more then an unfiltered one'); }); it('Saves variable results', function () { this.timeout(10000); const timeWithoutExtraSteps = timeXPath('(/descendant::*) => count() > 10', document); // Variables should only be evaluated once, not n times chai.assert.isAtMost( timeXPath('let $c := (/descendant::*) => count() return $c + $c + $c + $c + $c + $c', document), timeWithoutExtraSteps * 3); }); it('can memoize context free expressions', () => { // The filters use no context, so they must be instant chai.assert.isAtMost(timeXPath('(1 to 10000)[1 mod 2][1] or true()', document), 15); }); } describe('performance of descendant axis', () => { describe('in browser DOM', () => runTests(window.document.implementation.createDocument(null, null))); describe('in slimdom', () => runTests(new slimdom.Document())); });