yay format change

This commit is contained in:
Mark Nadal 2023-11-10 04:24:26 -08:00
parent 9ceb86b208
commit 96b1402a65
4 changed files with 630 additions and 102 deletions

View File

@ -16,7 +16,8 @@ var sT = setTimeout, B = sT.Book || (sT.Book = function(text){
//b.all[word] = {is: word}; return b;
return b.set(word, is);
};
b.list = [{from: text, size: (text||'').length, substring: sub, toString: to, book: b}];
// TODO: if from text, preserve the separator symbol.
b.list = [{from: text, size: (text||'').length, substring: sub, toString: to, book: b, get: b}];
b.page = page;
b.set = set;
b.get = get;
@ -25,8 +26,9 @@ var sT = setTimeout, B = sT.Book || (sT.Book = function(text){
}), PAGE = 2**12;
function page(word){
var b = this, l = b.list, i = spot(B.encode(word), l), p = l[i];
if('string' == typeof p){ l[i] = p = {size: -1, first: p, substring: sub, toString: to, book: b} } // TODO: test, how do we arrive at this condition again?
var b = this, l = b.list, i = spot(word, l), p = l[i];
if('string' == typeof p){ l[i] = p = {size: -1, first: p, substring: sub, toString: to, book: b, get: b} } // TODO: test, how do we arrive at this condition again?
p.i = i;
return p;
// TODO: BUG! What if we get the page, it turns out to be too big & split, we must then RE get the page!
}
@ -38,21 +40,27 @@ function get(word){
// get does an exact match, so we would have found it already, unless parseless page:
var page = b.page(word), l, has, a, i;
if(!page || !page.from){ return } // no parseless data
if(l = from(page)){ has = l[i = spot(B.encode(word), l)] }
return got(word, page);
}
function got(word, page){
var b = page.book, l, has, a, i;
if(l = from(page)){ has = l[got.i = i = spot(word, l, B.decode)]; } // TODO: POTENTIAL BUG! This assumes that each word on a page uses the same serializer/formatter/structure.
// parseless may return -1 from actual value, so we may need to test both. // TODO: Double check? I think this is correct.
if(has && word == has.word){ return (b.all[word] = has).is }
if('string' != typeof has){ has = l[got.i = i+=1] }
if(has && word == has.word){ return (b.all[word] = has).is }
if('string' != typeof has){ return }
a = slot(has) // Escape!
if(word != a[0]){
has = l[i+=1]; // edge case bug?
a = slot(has) // edge case bug?
if(word != a[0]){ return }
if(word != B.decode(a[0])){
has = l[got.i = i+=1]; // edge case bug?
a = slot(has); // edge case bug?
if(word != B.decode(a[0])){ return }
}
has = l[i] = b.all[word] = {word: word, is: a[1], page: page, substring: subt, toString: tot}; // TODO: convert to a JS value!!! Maybe index! TODO: BUG word needs a page!!!! TODO: Check for other types!!!
has = l[i] = b.all[word] = {word: word, is: B.decode(a[1]), page: page, substring: subt, toString: tot}; // TODO: convert to a JS value!!! Maybe index! TODO: BUG word needs a page!!!! TODO: Check for other types!!!
return has.is;
}
function spot(word, sorted){
function spot(word, sorted, parse){ parse = parse || spot.no || (spot.no = function(t){ return t }); // TODO: BUG???? Why is there substring()||0 ? // TODO: PERF!!! .toString() is +33% faster, can we combine it with the export?
var L = sorted, min = 0, page, found, l = word.length, max = L.length, i = max/2;
while((word < (page = (L[i=i>>0]||'').substring()) || ((L[i+1]||'').substring()||0) <= word) && i != min){ // L[i] <= word < L[i+1]
while((word < (page = parse((L[i=i>>0]||'').substring())) || parse((L[i+1]||'').substring()) <= word) && i != min){ // L[i] <= word < L[i+1]
i += (page < word)? (max - (min = i))/2 : -((max = i) - min)/2;
}
return i;
@ -60,7 +68,8 @@ function spot(word, sorted){
function from(a, t, l){
if('string' != typeof a.from){ return a.from }
(l = a.from = (t = a.from||'').substring(1, t.length-1).split(t[0])).toString = join; // slot
//(l = a.from = (t = a.from||'').substring(1, t.length-1).split(t[0])).toString = join; // slot
(l = a.from = slot(t = t||a.from||'')).toString = join;
return l;
}
@ -74,21 +83,23 @@ function set(word, is){
}
// MUST be an insert:
has = b.all[word] = {word: word, is: is, page: page, substring: subt, toString: tot};
page.first = (page.first < (tmp = B.encode(word)))? page.first : tmp;
if(!page.list){ (page.list = []).toString = join }
page.list.push(has);
page.sort = 1;
page.first = (page.first < word)? page.first : word;
if(!page.limbo){ (page.limbo = []).toString = join }
page.limbo.push(has);
b(word, is);
page.size += size(word) + size(is);
if(PAGE < page.size){ split(page, b) }
if((b.PAGE || PAGE) < page.size){ split(page, b) }
return b;
}
function split(p, b){
function split(p, b){ // TODO: use closest hash instead of half.
//console.time();
var L = p.list = p.list.sort(), l = L.length, i = l/2 >> 0, j = i, half = L[j], tmp;
// TODO: BUG???? May need to do a SORTED merge with FROM.
var i = 0, L = p.limbo, tmp;
//while(tmp = L[i++]){ }
var L = p.limbo = sort(p.limbo), l = L.length, i = l/2 >> 0, j = i, half = L[j], tmp;
//console.timeEnd();
var next = {list: [], first: B.encode(half.substring()), size: 0, substring: sub, toString: to, book: b}, nl = next.list;
var next = {limbo: [], first: half.substring(), size: 0, substring: sub, toString: to, book: b, get: b}, nl = next.limbo;
nl.toString = join;
//console.time();
while(tmp = L[i++]){
@ -98,38 +109,78 @@ function split(p, b){
}
//console.timeEnd();
//console.time();
p.list = p.list.slice(0, j);
p.limbo = p.limbo.slice(0, j);
p.size -= next.size;
p.sort = 0;
b.list.splice(spot(next.first, b.list)+1, 0, next);
b.list.splice(spot(next.first, b.list)+1, 0, next); // TODO: BUG! Make sure next.first is decoded text.
//console.timeEnd();
if(b.split){ b.split(next, p) }
}
function slot(t){ return (t=t||'').substring(1, t.length-1).split(t[0]) } B.slot = slot;
function size(t){ return (t||'').length||1 } // bits/numbers less size? Bug or feature?
function subt(i,j){ return this.word }
//function tot(){ return this.text = this.text || "'"+(this.word)+"'"+(this.is)+"'" }
function tot(){ var tmp;
if((tmp = this.page) && tmp.saving){ delete tmp.book.all[this.word]; }
return this.text = this.text || "'"+(this.word)+"'"+(this.is)+"'";
}
function sub(i,j){ return (this.first||this.word||(from(this)||'')[0]||'').substring(i,j) }
function to(){ return this.text = this.text || text(this) }
function join(){ return this.join('|') }
function text(p){
if(!p.list){ return (p.from||'')+'' }
if(!p.from){ return '|'+((p.list && (p.list = p.list.sort()).join('|'))||'')+'|' }
return '|'+from(p).concat(p.list).sort().join('|')+'|';
function slot(t){ return heal((t=t||'').substring(1, t.length-1).split(t[0]), t[0]) } B.slot = slot; // TODO: check first=last & pass `s`.
function heal(l, s){ var i, e;
if(0 > (i = l.indexOf(''))){ return l } // ~700M ops/sec on 4KB of Math.random()s, even faster if escape does exist.
if('' == l[0] && 1 == l.length){ return [] } // annoying edge cases! how much does this slow us down?
//if((c=i+2+parseInt(l[i+1])) != c){ return [] } // maybe still faster than below?
if((e=i+2+parseInt((e=l[i+1]).substring(0, e.indexOf('"'))||e)) != e){ return [] } // NaN check in JS is weird.
l[i] = l.slice(i, e).join(s||'|'); // rejoin the escaped value
return l.slice(0,i+1).concat(heal(l.slice(e), s)); // merge left with checked right.
}
B.encode = function(d, _){ _ = _ || "'";
if(typeof d == 'string'){
var i = d.indexOf(_), t = "";
while(i != -1){ t += _; i = d.indexOf(_, i+1) }
return t + _+d+_;
function size(t){ return (t||'').length||1 } // bits/numbers less size? Bug or feature?
function subt(i,j){ return subt.f? B.encode(this.word) : this.word }
//function tot(){ return this.text = this.text || "'"+(this.word)+"'"+(this.is)+"'" }
function tot(){ var tmp;
//if((tmp = this.page) && tmp.saving){ delete tmp.book.all[this.word]; } // TODO: BUG! Book can't know about RAD, this was from RAD, so this MIGHT be correct but we need to refactor. Make sure to add tests that will re-trigger this.
return this.text = this.text || ":"+B.encode(this.word)+":"+B.encode(this.is)+":";
//return this.text = this.text || "'"+(this.word)+"'"+(this.is)+"'";
}
function sub(i,j){ return (this.first||this.word||B.decode((from(this)||'')[0]||'')).substring(i,j) }
function to(){ return this.text = this.text || text(this) }
function join(){ return this.join('|') }
function text(p){ var l = p.limbo;
if(!l){ return (typeof p.from == 'string')? (p.from||'')+'' : '|'+p.from+'|' }
if(!p.from){ return p.limbo = null, '|'+((l && sort(l).join('|'))||'')+'|' } // TODO: p.limbo should be reset each time we "flush".
return '|'+mix(l, from(p), p).join('|')+'|'; // commenting out this sub-portion of code fixed a more basic test, but will probably cause a bug with a FROM + MEMORY.
}
function mix(l, f, p){ // TODO: IMPROVE PERFORMANCE!!!! l[j] = i is 5X+ faster than .push(
var j = 0, i;
while(i = l[j++]){
if(got(i.word, p)){
f[got.i] = i;
} else {
f.push(i);
}
}
return sort(f);
}
function sort(l){ //return l.sort();
return l.sort(function(a,b){
return (a.word||B.decode(''+a)) < (b.word||B.decode(''+b))? -1:1;
});
}
B.encode = function(d, s){ s = s || "|";
switch(typeof d){
case 'string': // text
var i = d.indexOf(s), c = 0;
while(i != -1){ c++; i = d.indexOf(s, i+1) }
return (c?s+c:'')+ '"' + d;
case 'number': return (d < 0)? ''+d : '+'+d;
case 'boolean': return d? '+' : '-';
case 'object': return d? "{TODO}" : ' ';
}
}
B.decode = function(t, s){ s = s || "|";
if('string' != typeof t){ return }
switch(t){ case ' ': return null; case '-': return false; case '+': return true; }
switch(t[0]){
case '-': case '+': return parseFloat(t);
case '"': return t.slice(1);
}
return t.slice(t.indexOf('"')+1);
}
B.hash = function(s, c){ // via SO
if(typeof s !== 'string'){ return }
c = c || 0; // CPU schedule hashing by
@ -142,5 +193,12 @@ B.hash = function(s, c){ // via SO
return c;
}
function record(key, val){ return key+B.encode(val)+"%"+key.length }
function decord(t){
var o = {}, i = t.lastIndexOf("%"), c = parseFloat(t.slice(i+1));
o[t.slice(0,c)] = B.decode(t.slice(c,i));
return o;
}
try{module.exports=B}catch(e){}
}());

View File

@ -1,17 +1,17 @@
;(function () { // RAD
;(function (){ // RAD
console.log("Warning: Experimental rewrite of RAD to use Book. It is not API compatible with RAD yet and is very alpha.");
var sT = setTimeout, Book = sT.Book, RAD = sT.RAD || (sT.RAD = function (opt) {
var sT = setTimeout, Book = sT.Book, RAD = sT.RAD || (sT.RAD = function (opt){
opt = opt || {};
opt.file = String(opt.file || 'radata');
var log = opt.log || nope;
var has = (sT.RAD.has || (sT.RAD.has = {}))[opt.file];
if (has) { return has }
var r = function rad(word, is, reply) {
if (!b) { start(word, is, reply); return r }
if (is === undefined || 'function' == typeof is) { // THIS IS A READ:
if(has){ return has }
var r = function rad(word, is, reply){
if(!b){ start(word, is, reply); return r }
if(is === undefined || 'function' == typeof is){ // THIS IS A READ:
var page = b.page(word);
if (page.from) {
if(page.from){
return is(null, page);
}
read(word, is, page); // get from disk
@ -20,107 +20,117 @@
//console.log("OFF");return;
// ON WRITE:
// batch until read from disk is done (and if a write was going, do that first)
//if(!valid(word, is, reply)){ return }
b(word, is);
write(word, reply);
return r;
}, /** @param b the book */ b;
async function read(word, reply, page) {
var p = page;//b.page(word);
async function read(word, reply, page){
var p = page || b.page(word);
reply = reply.call ? reply : () => { };
log(`read() ${word.slice(0, 40)}`);
get(p, function (err, disk) {
if (err) { log("ERR! in read() get() cb", err); reply(err); return }
get(p, function (err, disk){
if(err){ log("ERR! in read() get() cb", err); reply(err); return }
p.from = disk || p.from;
reply(null, p, b);
})
}
async function write(word, reply) {
async function write(word, reply){
log('write() word', word);
var p = b.page(word), tmp;
if (tmp = p.saving) { reply && tmp.push(reply); return } p.saving = [reply];
if(tmp = p.saving){ reply && tmp.push(reply); return } p.saving = [reply];
var S = +new Date; log(" writing", p.substring(), 'since last', S - p.saved, RAD.c, 'records', env.count++, 'mid-swap.');
get(p, function (err, disk) {
if (err) { log("ERR! in write() get() cb ", err); return }
get(p, function (err, disk){
if(err){ log("ERR! in write() get() cb ", err); return }
log(' get() - p.saving ', (p.saving || []).length);
if (p.from && disk) {
if(p.from && disk){
log(" get() merge: p.from ", p.toString().slice(0, 40), " disk.length", disk?.length || 0);
}
p.from = disk || p.from; // TODO: NEED TO MERGE! AND HANDLE ERR!
p.from = disk || p.from;
// p.list = p.text = p.from = 0;
// p.first = p.first.word || p.first;
tmp = p.saving; p.saving = [];
put(p, '' + p, function (err, ok) {
put(p, '' + p, function (err, ok){
env.count--; p.saved = +new Date; log(" ...wrote %d bytes in %dms", ('' + p).length, (p.saved = +new Date) - S);
if (!p.saving.length) { p.saving = 0; reply?.call && reply(err, ok); return; } p.saving = 0; // what?
// TODO: BUG: Confirmed! Only calls back first. Need to fix + use perf hack from old RAD.
sT.each(tmp, function(cb){ cb && cb(err, ok) });
if(!p.saving.length){ p.saving = 0; return; } //p.saving = 0; // what?
// log({ tmp });
console.log("hm?", word, reply+'');
write(word, reply);
});
})
}
function put(file, data, cb) {
function put(file, data, cb){
file.first && (file = Book.slot(file.first)[0]);
put[file = fname(file)] = { data: data };
RAD.put(file, data, function (err, ok) {
RAD.put(file, data, function (err, ok){
delete put[file];
cb && cb(err, ok);
});
};
function get(file, cb) {
function get(file, cb){
var tmp;
file.first && (file = Book.slot(file.first)[0]);
if (tmp = put[file = fname(file)]) { cb(u, tmp.data); return }
if (tmp = get[file]) { tmp.push(cb); return } get[file] = [cb];
RAD.get(file, function (err, data) {
if(tmp = put[file = fname(file)]){ cb(u, tmp.data); return }
if(tmp = get[file]){ tmp.push(cb); return } get[file] = [cb];
RAD.get(file, function (err, data){
tmp = get[file]; delete get[file];
var i = -1, f; while (f = tmp[++i]) { f(err, data) } // CPU SCHEDULE?
var i = -1, f; while (f = tmp[++i]){ f(err, data) } // TODO: BUG! CPU SCHEDULE?
});
};
function start(word, is, reply) {
if (b) { r(word, is, reply); return }
get(' ', function (err, d) {
if (err) { log('ERR! in start() get()', err); reply && reply(err); return }
if (b) { r(word, is, reply); return }
function start(word, is, reply){
if(b){ r(word, is, reply); return }
get(' ', function (err, d){
if(err){ log('ERR! in start() get()', err); reply && reply(err); return }
if(b){ r(word, is, reply); return }
//wrap(b = r.book = Book(d));
(b = r.book = Book()).list = Book.slot(d);
watch(b).list[0] = "'!'";
r(word, is, reply);
})
}
function watch(b) { // SPLIT LOGIC!
function watch(b){ // SPLIT LOGIC!
var split = b.split;
b.list.toString = function () {
b.list.toString = function (){
console.log("hi'");
console.time();
var i = -1, t = '', p; while (p = this[++i]) {
var i = -1, t = '', p; while (p = this[++i]){
t += "|" + p.substring();
}
t += "|";
console.timeEnd();
return t;
}
b.split = function (next, page) {
b.split = function (next, page){
log("SPLIT!!!!", b.list.length);
put(' ', '' + b.list, function (err, ok) {
if (err) { console.log("ERR!"); return }
put(' ', '' + b.list, function (err, ok){
if(err){ console.log("ERR!"); return }
// ??
});
}
return b;
}
function ename(t) { return encodeURIComponent(t).replace(/\*/g, '%2A').slice(0, 250) }
function fname(p) { return opt.file + '/' + ename(p.substring()) }
function ename(t){ return encodeURIComponent(t).replace(/\*/g, '%2A').slice(0, 250) }
function fname(p){ return opt.file + '/' + ename(p.substring()) }
function valid(word, is, reply){
if(is !== is){ reply(word +" cannot be NaN!"); return }
return true;
}
return r;
}), MAX = 1000/* 300000000 */;
try { module.exports = RAD } catch (e) { }
try { module.exports = RAD } catch (e){ }
// junk below that needs to be cleaned up and corrected for the actual correct RAD API.
var env = {}, nope = function () { }, nah = function () { return nope }, u;
var env = {}, nope = function (){ }, nah = function (){ return nope }, u;
env.require = (typeof require !== '' + u && require) || nope;
env.process = (typeof process != '' + u && process) || { memoryUsage: nah };
env.os = env.require('os') || { totalmem: nope, freemem: nope };
@ -135,7 +145,7 @@
//if(err && 'ENOENT' === (err.code||'').toUpperCase()){ err = null }
setInterval(function () {
setInterval(function (){
var stats = { memory: {} };
stats.memory.total = env.os.totalmem() / 1024 / 1024; // in MB
@ -148,44 +158,47 @@
}());
; (function () { // temporary fs storage plugin, needs to be refactored to use the actual RAD plugin interface.
; (function (){ // temporary fs storage plugin, needs to be refactored to use the actual RAD plugin interface.
var fs;
try { fs = require('fs') } catch (e) { };
if (!fs) { return }
try { fs = require('fs') } catch (e){ };
if(!fs){ return }
var sT = setTimeout, RAD = sT.RAD;
RAD.put = function (file, data, cb) {
RAD.put = function (file, data, cb){
fs.writeFile(file, data, cb);
}
RAD.get = function (file, cb) {
fs.readFile(file, function (err, data) {
if (err && 'ENOENT' === (err.code || '').toUpperCase()) { return cb() }
RAD.get = function (file, cb){
fs.readFile(file, function (err, data){
if(err && 'ENOENT' === (err.code || '').toUpperCase()){ return cb() }
cb(err, data.toString());
});
}
}());
;(function () { // temporary fs storage plugin, needs to be refactored to use the actual RAD plugin interface.
;(function (){ // temporary fs storage plugin, needs to be refactored to use the actual RAD plugin interface.
var lS;
try { lS = localStorage } catch (e) { };
if (!lS) { return }
try { lS = localStorage } catch (e){ };
if(!lS){ return }
var sT = setTimeout, RAD = sT.RAD;
RAD.put = function (file, data, cb) {
RAD.put = function (file, data, cb){
setTimeout(function(){
lS[file] = data;
cb(null, 1);
},9);
}
RAD.get = function (file, cb) {
RAD.get = function (file, cb){
setTimeout(function(){
cb(null, lS[file]);
},9);
}
}());
;(function(){
return;
;(function(){ return;
var get;
try { get = fetch } catch (e) { };
if (!get) { return }
try { get = fetch } catch (e){ };
if(!get){ return }
var sT = setTimeout, RAD = sT.RAD;
RAD.put = function(file, data, cb){ cb(401) }

403
test/rad/book.js Normal file
View File

@ -0,0 +1,403 @@
var root;
var Gun;
(function(){
var env;
if(typeof global !== 'undefined'){ env = global }
if(typeof window !== 'undefined'){ env = window }
root = env.window? env.window : global;
try{ env.window && root.localStorage && root.localStorage.clear() }catch(e){}
//try{ indexedDB.deleteDatabase('radatatest') }catch(e){}
if(root.Gun){
root.Gun = root.Gun;
root.Gun.TESTING = true;
} else {
try{ require('fs').unlinkSync('data.json') }catch(e){}
try{ require('../../lib/fsrm')('radatatest') }catch(e){}
root.Gun = require('../../gun');
root.Gun.TESTING = true;
require('../../lib/store');
require('../../lib/rfs');
}
try{ var expect = global.expect = require("../expect") }catch(e){}
}(this));
;(function(){
Gun = root.Gun
describe('RAD', function(){
var names = ["Adalard","Adora","Aia","Albertina","Alfie","Allyn","Amabil","Ammamaria","Andy","Anselme","Ardeen","Armand","Ashelman","Aube","Averyl","Baker","Barger","Baten","Bee","Benia","Bernat","Bevers","Bittner","Bobbe","Bonny","Boyce","Breech","Brittaney","Bryn","Burkitt","Cadmann","Campagna","Carlee","Carver","Cavallaro","Chainey","Chaunce","Ching","Cianca","Claudina","Clyve","Colon","Cooke","Corrina","Crawley","Cullie","Dacy","Daniela","Daryn","Deedee","Denie","Devland","Dimitri","Dolphin","Dorinda","Dream","Dunham","Eachelle","Edina","Eisenstark","Elish","Elvis","Eng","Erland","Ethan","Evelyn","Fairman","Faus","Fenner","Fillander","Flip","Foskett","Fredette","Fullerton","Gamali","Gaspar","Gemina","Germana","Gilberto","Giuditta","Goer","Gotcher","Greenstein","Grosvenor","Guthrey","Haldane","Hankins","Harriette","Hayman","Heise","Hepsiba","Hewie","Hiroshi","Holtorf","Howlond","Hurless","Ieso","Ingold","Isidora","Jacoba","Janelle","Jaye","Jennee","Jillana","Johnson","Josy","Justinian","Kannan","Kast","Keeley","Kennett","Kho","Kiran","Knowles","Koser","Kroll","LaMori","Lanctot","Lasky","Laverna","Leff","Leonanie","Lewert","Lilybel","Lissak","Longerich","Lou","Ludeman","Lyman","Madai","Maia","Malvina","Marcy","Maris","Martens","Mathilda","Maye","McLain","Melamie","Meras","Micco","Millburn","Mittel","Montfort","Moth","Mutz","Nananne","Nazler","Nesta","Nicolina","Noellyn","Nuli","Ody","Olympie","Orlena","Other","Pain","Parry","Paynter","Pentheas","Pettifer","Phyllida","Plath","Posehn","Proulx","Quinlan","Raimes","Ras","Redmer","Renelle","Ricard","Rior","Rocky","Ron","Rosetta","Rubia","Ruttger","Salbu","Sandy","Saw","Scholz","Secor","September","Shanleigh","Shenan","Sholes","Sig","Sisely","Soble","Spanos","Stanwinn","Stevie","Stu","Suzanne","Tacy","Tanney","Tekla","Thackeray","Thomasin","Tilla","Tomas","Tracay","Tristis","Ty","Urana","Valdis","Vasta","Vezza","Vitoria","Wait","Warring","Weissmann","Whetstone","Williamson","Wittenburg","Wymore","Yoho","Zamir","Zimmermann"];
var opt = {};
opt.file = 'radatatest';
var RAD = (setTimeout.RAD) || require('../../lib/radisk');
var Book = (setTimeout.Book) || require('../../lib/book');
//opt.store = ((Gun.window && Gun.window.RindexedDB) || require('../../lib/rfs'))(opt);
opt.chunk = 1000;
var rad = RAD(opt), esc = String.fromCharCode(27);
describe('Book', function(){
this.timeout(1000 * 9);
/*it('parse', function(done){
this.timeout(60000);
if(Gun.window){ return done() }
var raw = require('fs').readFileSync(__dirname + '/parse.rad').toString();
rad.parse('!', function(err, disk){
console.log("!!!!", err);
}, raw);
return;
});*/
it('deleting old RAD tests (may take long time)', function(done){
done(); // Mocha doesn't print test until after its done, so show this first.
});
it('deleted', function(done){
this.timeout(60 * 1000);
if(!Gun.window){ return done() }
root.localStorage && root.localStorage.clear();
//await new Promise(function(res){ indexedDB.deleteDatabase('radatatest').onsuccess = function(e){ res() } } );
indexedDB.deleteDatabase('radatatest').onsuccess = function(e){ done() }
});
describe('Book Format', function(done){
var B = Book;
it('encode decode', function(){
expect(B.decode(B.encode(null))).to.be(null);
expect(B.decode(B.encode(false))).to.be(false);
expect(B.decode(B.encode(true))).to.be(true);
expect(B.decode(B.encode(0))).to.be(0);
expect(B.decode(B.encode(-Infinity))).to.be(-Infinity);
expect(B.decode(B.encode(Infinity))).to.be(Infinity);
expect(B.decode(B.encode(1))).to.be(1);
expect(B.decode(B.encode(2))).to.be(2);
expect(B.decode(B.encode(1.2))).to.be(1.2);
expect(B.decode(B.encode(1234.56789))).to.be(1234.56789);
expect(B.decode(B.encode(''))).to.be('');
expect(B.decode(B.encode("hello world"))).to.be("hello world");
expect(B.decode(B.encode("he||o"))).to.be("he||o");
expect(B.decode(B.encode("ho|y ha|o"))).to.be("ho|y ha|o");
expect(B.decode(B.encode("he||||y"))).to.be("he||||y");
expect(B.decode(B.encode("ho|\|ow"))).to.be("ho|\|ow");
expect(B.decode(B.encode("so\\rrow"))).to.be("so\\rrow");
expect(B.decode(B.encode("bo\\|\|row"))).to.be("bo\\|\|row");
expect(B.decode(B.encode("||\áãbbçcddéẽffǵghhíĩj́jḱkĺlḿmńñóõṕpqqŕrśsttẃwúǘũxxýỹźzàbcdèfghìjklm̀ǹòpqrstùǜẁxỳz|"))).to.be("||\áãbbçcddéẽffǵghhíĩj́jḱkĺlḿmńñóõṕpqqŕrśsttẃwúǘũxxýỹźzàbcdèfghìjklm̀ǹòpqrstùǜẁxỳz|");
});
it('heal', function(){
//var obj = {a: null, b: false, c: true, d: 0, e: 42, f: Infinity, h: "hello"};
var page = '| |-|+|'+B.encode('he||o!')+'|+0|+42.69|'+B.encode('he|p')+'|+Infinity|';
expect(B.slot(page)).to.be.eql([' ', '-', '+', '|2"he||o!', '+0', '+42.69', '|1"he|p', '+Infinity']);
});
it.skip('encode decode object', function(){
expect(B.decode(B.encode({foo: 'bar', a: 1}))).to.be.eql({foo: 'bar', a: 1})
});
});
describe('BASIC API', function(done){
// TODO: Mark return here, slot("") slot("ab") causes infinite loop with heal, so need to detect not corrupted yet.
it('write', function(done){
rad('hello', 'world', function(err, ok){
expect(err).to.not.be.ok();
done();
});
});
it('read', function(done){
rad('hello', function(err, page){
var val = page.get('hello');
expect(val).to.be('world');
done();
})
});
});
var prim = [
null,
'string',
728858,
BigInt(1000000000000000000000000000000000000000000000000000000000n),
true,
false,
-Infinity,
Infinity,
-0
];
//var prim = ['alice', 'bob'];
//var prim = [null];
root.rad = rad;
describe('can in-memory write & read all primitives', done => { prim.forEach(function(type){
var b = setTimeout.Book();
it('save '+type, done => { setTimeout(function(){
b('type-'+type, type);
var val = b('type-'+type);
expect(val).to.be(type);
done();
},1); });
});});
describe('can disk write & read all primitives', done => { prim.forEach(function(type){
it('save '+type, done => { setTimeout(function(){
rad('type-'+type, type, function(err, ok){
expect(err).to.not.be.ok();
rad('type-'+type, function(err, page){
var val = page.get('type-'+type);
expect(val).to.be(type);
done();
});
});
},1); });
});});
describe('error on invalid primitives', function(){
it('test invalid', done => {
rad('type-NaN', NaN, function(err, ok){
expect(err).to.be.ok();
done();
});
});
});
describe('Async Race Conditions', function(){
it('make sure word does not get duplicated when data is re-saved after read', done => {
var opt = {file: 'zadata'}
var prev = RAD(opt);
prev('helloz', 'world', function(err, ok){
prev('helloz', function(err, page){
prev('zalice', 'yay', function(err){
expect(page.text.split('helloz').length).to.be(2);
done();
});
});
});
/*
(A) READ ONLY: we receive a message, we READ only - parseless is important.
(B) READ & WRITE: we write a page, and it already exists on disk.
(C) WRITE ONLY: we write a page, and it is new to disk.
*/
});
it('make sure word does not get duplicated when data is re-saved after read <', done => {
var opt = {file: 'azadata'}
var prev = RAD(opt);
prev('helloz', 'world', function(err, ok){
prev('helloz', function(err, page){
prev('azalice', 'yay', function(err){
expect(page.text.split('helloz').length).to.be(2);
done();
});
});
});
/*
(A) READ ONLY: we receive a message, we READ only - parseless is important.
(B) READ & WRITE: we write a page, and it already exists on disk.
(C) WRITE ONLY: we write a page, and it is new to disk.
*/
});
it('test if adding an in-memory word merges with previously written disk data', done => {
var prev = RAD(opt);
prev('pa-alice', 'hello', function(err, ok){
expect(err).to.not.be.ok();
setTimeout(function(){
var rad = RAD(opt);
rad('pa-bob', 'banana', function(err, ok){
expect(err).to.not.be.ok();
var text = rad.book.list[0].text;
var i = text.indexOf('pa-alice');
expect(i).to.not.be(-1);
var ii = text.indexOf('hello');
expect((ii - i) < ('pa-alice'.length + 3)).to.be.ok();
done();
})
},99);
});
});
it('test if adding an in-memory word merges with previously written disk data <', done => {
var opt = {file: 'azadatab'}
var prev = RAD(opt);
prev('pa-alice', 'hello', function(err, ok){
expect(err).to.not.be.ok();
setTimeout(function(){
var rad = RAD(opt);
rad('pa-alex', 'banana', function(err, ok){
expect(err).to.not.be.ok();
var text = rad.book.list[0].text;
var i = text.indexOf('pa-alice');
expect(i).to.not.be(-1);
var ii = text.indexOf('hello');
expect((ii - i) < ('pa-alice'.length + 3)).to.be.ok();
done();
})
},99);
});
});
it('test if adding an in-memory escaped word merges with previously written disk data', done => {
var opt = {file:'badata'};
var prev = RAD(opt);
prev('ba-bob', 'hello', function(err, ok){
expect(err).to.not.be.ok();
setTimeout(function(){
var rad = RAD(opt);
rad('ba-a|ice', 'banana', function(err, ok){
expect(err).to.not.be.ok();
var text = rad.book.list[0].text;
var i = text.indexOf('ba-a|ice');
expect(i).to.not.be(-1);
var ii = text.indexOf('banana');
expect((ii - i) < ('ba-a|ice'.length + 3)).to.be.ok();
var iii = text.indexOf('ba-bob');
if(iii < i){ console.log("ERROR! Escaped word not sorted correctly!!!") }
expect(iii > i).to.be.ok();
done();
})
},99);
});
});
it('test if updating an in-memory word merges with previously written disk data', done => {
var opt = {file:'pu-data'};
var prev = RAD(opt);
prev('pu-zach', 'zap');
prev('pu-alex', 'yay');
prev('pu-alice', 'hello', function(err, ok){
expect(err).to.not.be.ok();
var rad = RAD(opt);
rad('pu-alice', 'cool', function(err, ok){
expect(err).to.not.be.ok();
//return;
var next = RAD(opt);
next('pu-alice', function(err, page){
expect('cool').to.be(page.get('pu-alice'));
done();
})
});
});
});
});
describe('Recursive Book Lookups', function(){
function gen(val){ return val + String.random(99,'a') }
var opt = {file: 'gen'}
//var rad = window.names = Book();
var rad = window.names = RAD(opt);
it('Generate more than 1 page', done => {
var i = 0;
names.forEach(function(name){
name = name.toLowerCase();
rad(name, gen(name));
clearTimeout(done.c)
done.c = setTimeout(done, 99);
});
});
it('Make sure parseless lookup works with incrementally parsed values', done => {
rad = RAD(opt);
rad('adora', function(err, page){
var n = page.get('adora');
expect(gen('adora')).to.be(n);
rad('aia', function(err, page){
var n = page.get('aia');
expect(gen('aia')).to.be(n);
done();
});
});
});
it('Read across the pages', done => {
rad = RAD(opt);
names.forEach(function(name){
name = name.toLowerCase();
rad(name, function(err, page){
var n = page.get(name);
expect(gen(name)).to.be(n);
clearTimeout(done.c);
done.c = setTimeout(done, 99);
});
});
console.log("TODO: BUG!!! MARK & ROGOWSKI COME BACK HERE: NOTICED THAT INDEX IS NOT ESCAPED ALTHO THERE MAY BE OTHER THINGS TO DO FIRST!!!");
});
/*it.skip('Correctly calculate size', done => {
var r = String.random(1000);
rad('a', r);
r = String.random(2000);
rad('b', r);
r = String.random(3000);
rad('c', r);
});*/
});
});
var ntmp = names;
describe.skip('RAD + GUN', function(){ return;
this.timeout(1000 * 9);
var ochunk = 1000;
Gun.on('opt', function(root){
root.opt.localStorage = false;
Gun.window && console.log("RAD disabling localStorage during tests.");
this.to.next(root);
})
var gun = Gun({chunk: ochunk});
/*it('deleting old tests (may take long time)', function(done){
done(); // Mocha doesn't print test until after its done, so show this first.
}); it('deleted', function(done){
this.timeout(60 * 1000);
if(!Gun.window){ return done() }
indexedDB.deleteDatabase('radatatest').onsuccess = function(e){ done() }
});*/
it('write contacts', function(done){
var all = {}, to, start, tmp;
names.forEach(function(v,i){
all[++i] = true;
tmp = v.toLowerCase();
gun.get('names').get(tmp).put({name: v, age: i}, function(ack){
expect(ack.err).to.not.be.ok();
delete all[i];
if(!Object.empty(all)){ return }
done();
})
})
});
});
});
}());

54
test/rad/mocha.html Normal file
View File

@ -0,0 +1,54 @@
<html>
<head>
<title>Gun Tests</title>
<link rel="stylesheet" href="../mocha.css"/>
<style>
</style>
</head>
</head>
<body>
Gun Tests
<div id="debug"></div>
<div id="mocha"></div>
<script src="../json2.js"></script>
<script src="../mocha.js"></script>
<script>mocha.setup({ui:'bdd',globals:[]});</script>
<script src="../expect.js"></script>
<script></script>
<script src="../../gun.js"></script>
<script src="../../lib/book.js"></script>
<script src="../../lib/radisk3.js"></script>
<script src="./book.js"></script>
<script>
if(location.search){
Gun.debug = true;
console.log('async?', Gun.debug);
}
var run = mocha.run(function(a,b,c){
document.body.prepend("TODO: localStorage gun/gap ???");return;
var yes = confirm("REFRESH BROWSER FOR ASYNC TESTS?");
if(yes){
if(location.search){
location.search = '';
} else {
location.search = '?async';
}
return;
}
/*console.log("???????????", a);
//if(a !== 0){ return }
document.getElementById('mocha-stats').id = 'mocha-stats2';
document.getElementById('mocha-report').style.display = 'none';
document.getElementById('mocha-report').id = 'mocha-report2';
Gun.debug = false;
mocha.run();*/
});
run.on("fail", function(test, err){
console.log("!!!!!!!!!!!", test, err);
//alert(5);
})
</script>
</body>
</html>