diff options
Diffstat (limited to 'docs/tests/hntrie-test.html')
-rw-r--r-- | docs/tests/hntrie-test.html | 226 |
1 files changed, 226 insertions, 0 deletions
diff --git a/docs/tests/hntrie-test.html b/docs/tests/hntrie-test.html new file mode 100644 index 0000000..ada8ecd --- /dev/null +++ b/docs/tests/hntrie-test.html @@ -0,0 +1,226 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<meta name="viewport" content="width=device-width, initial-scale=1"> +</head> +<body style="font: 14px sans-serif"> +<h1>HNTrieContainer test</h1> +<p><a href="./.">Back</a></p> +<p> </p> +<div><button id="test" type="button">Test!</button></div> +<div id="stdout"></div> +<script src="https://rawcdn.githack.com/gorhill/uBlock/1b6fea16da81d1df3e2efd5a31894f71ea04dbb1/src/js/hntrie.js"></script> +<!-- <script src="../../src/js/hntrie.js"></script> --> +<script src="hostname-pool.js"></script> +<script> +const createRandomLabel = function() { + return Math.random().toString(36).slice(2); +}; + +const stdout = function(s) { + const line = document.createElement('div'); + const parent = document.getElementById('stdout'); + let tooManyErrors = false; + if ( parent.childElementCount > 100 ) { + tooManyErrors = true; + line.textContent = 'Too many errors, aborting'; + } else { + line.textContent = s; + } + parent.appendChild(line) + if ( tooManyErrors ) { + throw 'Aborting'; + } +} + +/******************************************************************************/ + +// Dictionary of hostnames +// +const FilterHostnameDict = function(hostnames) { + this.h = ''; // short-lived register + this.dict = new Set(); + if ( hostnames !== undefined ) { + this.fromIterable(hostnames); + } +}; + +FilterHostnameDict.prototype = { + add: function(hn) { + if ( this.dict.has(hn) ) { return false; } + this.dict.add(hn); + return true; + }, + fromIterable: function(hostnames) { + for ( let hn of hostnames ) { + this.add(hn); + } + return this; + }, + matches: function(needle) { + while ( this.dict.has(needle) === false ) { + const pos = needle.indexOf('.'); + if ( pos === -1 ) { + this.h = ''; + return false; + } + needle = needle.slice(pos + 1); + } + this.h = needle; + return true; + }, +}; + +/******************************************************************************/ + +const testFlavor = function(hostnames, name, matchesFn, hitFn) { + stdout('\xA0'); + stdout('Testing ' + name + '...'); + + const t0 = performance.now(); + + for ( let i = 0; i < hostnames.length; i++ ) { + // Exact hits + let needle = hostnames[i]; + if ( hitFn(matchesFn(needle)) === false ) { + stdout('Exact hits failed: ' + needle); + } + + // Subdomain hits + needle = createRandomLabel() + '.' + hostnames[i]; + if ( hitFn(matchesFn(needle)) === false ) { + stdout('Subdomain hits failed: ' + needle); + } + + // Misses batch 1 + needle = createRandomLabel() + '.com'; + if ( hitFn(matchesFn(needle)) !== false ) { + stdout('Misses batch 1: ' + needle); + } + + // Misses batch 2 + needle = hostnames[i] + '.' + createRandomLabel(); + if ( hitFn(matchesFn(needle)) !== false ) { + stdout('Misses batch 2: ' + needle); + } + + // Misses batch 3 + needle = hostnames[i]; + let pos = needle.lastIndexOf('.'); + if ( pos !== -1 ) { + needle = needle.slice(0, pos) + needle.slice(pos + 1); + if ( hitFn(matchesFn(needle)) !== false ) { + stdout('Misses batch 3: ' + needle); + } + } + } + + const t1 = performance.now(); + + stdout( + name + ': ' + + (hostnames.length * 5).toLocaleString() + + ' tests completed in ' + + (t1 - t0).toFixed(2) + ' ms' + ); +}; + +const hnBigTrieJS = new HNTrieContainer(); +const hnBigTrieWASM = new HNTrieContainer(); +const hnBigTrieUnserialized = new HNTrieContainer(); + +Promise.all([ + hnBigTrieJS.readyToUse(), + hnBigTrieWASM.readyToUse() +]).then(( ) => { + let t0 = performance.now(); + const theSet = new FilterHostnameDict(hostnamePool); + let t1 = performance.now(); + stdout('\xA0'); + stdout( + 'Set creation completed in ' + + (t1 - t0).toFixed(2) + ' ms' + ); + + t0 = performance.now(); + const theTrieJS = hnBigTrieJS.fromIterable(hostnamePool, 'addJS'); + hnBigTrieJS.optimize(); + t1 = performance.now(); + stdout('\xA0'); + stdout( + 'HNTrieContainer creation (JS) completed in ' + + (t1 - t0).toFixed(2) + ' ms' + ); + + let theTrieWASM; + if ( hnBigTrieWASM.addWASM instanceof Function ) { + t0 = performance.now(); + theTrieWASM = hnBigTrieWASM.fromIterable(hostnamePool, 'addWASM'); + hnBigTrieWASM.optimize(); + t1 = performance.now(); + stdout('\xA0'); + stdout( + 'HNTrieContainer creation (WASM) completed in ' + + (t1 - t0).toFixed(2) + ' ms' + ); + + const bufJS = theTrieJS.container.buf; + const bufWASM = theTrieWASM.container.buf; + for ( let i = 0; i < bufJS.length; i++ ) { + if ( bufJS[i] !== bufWASM[i] ) { + stdout('theTrieWASM failure at index ' + i); + break; + } + } + } + + let selfie = hnBigTrieJS.serialize(); + t0 = performance.now(); + hnBigTrieUnserialized.unserialize(selfie); + const theTrieUnserialized = hnBigTrieUnserialized.createOne(hnBigTrieJS.compileOne(theTrieJS)); + t1 = performance.now(); + stdout('\xA0'); + stdout( + 'HNTrieContainer creation (unserialized) completed in ' + + (t1 - t0).toFixed(2) + ' ms' + ); + selfie = undefined; + + document.getElementById('test').addEventListener('click', ( ) => { + let parent = document.getElementById('stdout'); + while ( parent.childElementCount !== 0 ) { + parent.removeChild(parent.firstChild); + } + testFlavor( + hostnamePool, + 'Set (JS)', + theSet.matches.bind(theSet), + r => r + ); + testFlavor( + hostnamePool, + 'HNTrieContainer (JS)', + theTrieJS.matchesJS.bind(theTrieJS), + r => r >= 0 + ); + if ( theTrieWASM !== undefined ) { + testFlavor( + hostnamePool, + 'HNTrieContainer (WASM)', + theTrieWASM.matchesWASM.bind(theTrieWASM), + r => r >= 0 + ); + } + testFlavor( + hostnamePool, + 'HNTrieContainer (unserialized)', + theTrieUnserialized.matchesJS.bind(theTrieUnserialized), + r => r >= 0 + ); + }); +}); + +</script> +</body> +</html> |