summaryrefslogtreecommitdiffstats
path: root/docs/tests/hntrie-test.html
diff options
context:
space:
mode:
Diffstat (limited to 'docs/tests/hntrie-test.html')
-rw-r--r--docs/tests/hntrie-test.html226
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>&nbsp;</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>