'use strict';
(function(){
const T = self.SqliteTestUtil;
const SW = new Worker("jswasm/sqlite3-worker1.js");
const DbState = {
id: undefined
};
const eOutput = document.querySelector('#test-output');
const log = console.log.bind(console);
const logHtml = function(cssClass,...args){
log.apply(this, args);
const ln = document.createElement('div');
if(cssClass) ln.classList.add(cssClass);
ln.append(document.createTextNode(args.join(' ')));
eOutput.append(ln);
};
const warn = console.warn.bind(console);
const error = console.error.bind(console);
const toss = (...args)=>{throw new Error(args.join(' '))};
SW.onerror = function(event){
error("onerror",event);
};
let startTime;
const MsgHandlerQueue = {
queue: [],
id: 0,
push: function(type,callback){
this.queue.push(callback);
return type + '-' + (++this.id);
},
shift: function(){
return this.queue.shift();
}
};
const testCount = ()=>{
logHtml("","Total test count:",T.counter+". Total time =",(performance.now() - startTime),"ms");
};
const logEventResult = function(ev){
const evd = ev.result;
logHtml(evd.errorClass ? 'error' : '',
"runOneTest",ev.messageId,"Worker time =",
(ev.workerRespondTime - ev.workerReceivedTime),"ms.",
"Round-trip event time =",
(performance.now() - ev.departureTime),"ms.",
(evd.errorClass ? evd.message : "") );
};
const runOneTest = function(eventType, eventArgs, callback){
T.assert(eventArgs && 'object'===typeof eventArgs);
const messageId = MsgHandlerQueue.push(eventType,function(ev){
logEventResult(ev);
if(callback instanceof Function){
callback(ev);
testCount();
}
});
const msg = {
type: eventType,
args: eventArgs,
dbId: DbState.id,
messageId: messageId,
departureTime: performance.now()
};
log("Posting",eventType,"message to worker dbId="+(DbState.id||'default')+':',msg);
SW.postMessage(msg);
};
const dbMsgHandler = {
open: function(ev){
DbState.id = ev.dbId;
log("open result",ev);
},
exec: function(ev){
log("exec result",ev);
},
export: function(ev){
log("export result",ev);
},
error: function(ev){
error("ERROR from the worker:",ev);
logEventResult(ev);
},
resultRowTest1: function f(ev){
if(undefined === f.counter) f.counter = 0;
if(null === ev.rowNumber){
T.assert(undefined === ev.row)
.assert(Array.isArray(ev.columnNames))
.assert(ev.columnNames.length);
}else{
T.assert(ev.rowNumber > 0);
++f.counter;
}
T.assert(null === ev.rowNumber || 'number' === typeof ev.row.b);
}
};
const runTests2 = function(){
const mustNotReach = ()=>{
throw new Error("This is not supposed to be reached.");
};
runOneTest('exec',{
sql: ["create table t(a,b);",
"insert into t(a,b) values(1,2),(3,4),(5,6)"
],
resultRows: [], columnNames: []
}, function(ev){
ev = ev.result;
T.assert(0===ev.resultRows.length)
.assert(0===ev.columnNames.length);
});
runOneTest('exec',{
sql: 'select a a, b b from t order by a',
resultRows: [], columnNames: [], saveSql:[]
}, function(ev){
ev = ev.result;
T.assert(3===ev.resultRows.length)
.assert(1===ev.resultRows[0][0])
.assert(6===ev.resultRows[2][1])
.assert(2===ev.columnNames.length)
.assert('b'===ev.columnNames[1]);
});
runOneTest('exec',{
sql: 'select a a, b b from t order by a',
resultRows: [], columnNames: [],
rowMode: 'object'
}, function(ev){
ev = ev.result;
T.assert(3===ev.resultRows.length)
.assert(1===ev.resultRows[0].a)
.assert(6===ev.resultRows[2].b)
});
runOneTest('exec',{sql:'intentional_error'}, mustNotReach);
runOneTest('exec',{
sql:'select 1',
resultRows: [],
}, function(ev){
ev = ev.result;
T.assert(1 === ev.resultRows.length)
.assert(1 === ev.resultRows[0][0]);
});
runOneTest('exec',{
sql: 'select a a, b b from t order by a',
callback: 'resultRowTest1',
rowMode: 'object'
}, function(ev){
T.assert(3===dbMsgHandler.resultRowTest1.counter);
dbMsgHandler.resultRowTest1.counter = 0;
});
runOneTest('exec',{
sql:[
"pragma foreign_keys=0;",
"select a, b from t order by a desc;",
"select a from t;"
],
rowMode: 1,
resultRows: []
},function(ev){
const rows = ev.result.resultRows;
T.assert(3===rows.length).
assert(6===rows[0]);
});
runOneTest('exec',{sql: 'delete from t where a>3'});
runOneTest('exec',{
sql: 'select count(a) from t',
resultRows: []
},function(ev){
ev = ev.result;
T.assert(1===ev.resultRows.length)
.assert(2===ev.resultRows[0][0]);
});
runOneTest('export',{}, function(ev){
ev = ev.result;
log("export result:",ev);
T.assert('string' === typeof ev.filename)
.assert(ev.byteArray instanceof Uint8Array)
.assert(ev.byteArray.length > 1024)
.assert('application/x-sqlite3' === ev.mimetype);
});
runOneTest('close',{unlink:true},function(ev){
ev = ev.result;
T.assert('string' === typeof ev.filename);
});
runOneTest('close',{unlink:true},function(ev){
ev = ev.result;
T.assert(undefined === ev.filename);
logHtml('warning',"This is the final test.");
});
logHtml('warning',"Finished posting tests. Waiting on async results.");
};
const runTests = function(){
const waitForOpen = 1,
simulateOpenError = 0 ;
logHtml('',
"Sending 'open' message and",(waitForOpen ? "" : "NOT ")+
"waiting for its response before continuing.");
startTime = performance.now();
runOneTest('open', {
filename:'testing2.sqlite3',
simulateError: simulateOpenError
}, function(ev){
log("open result",ev);
T.assert('testing2.sqlite3'===ev.result.filename)
.assert(ev.dbId)
.assert(ev.messageId)
.assert('string' === typeof ev.result.vfs);
DbState.id = ev.dbId;
if(waitForOpen) setTimeout(runTests2, 0);
});
if(!waitForOpen) runTests2();
};
SW.onmessage = function(ev){
if(!ev.data || 'object'!==typeof ev.data){
warn("Unknown sqlite3-worker message type:",ev);
return;
}
ev = ev.data;
if(ev.result && ev.messageId){
const f = MsgHandlerQueue.shift();
if('error'===ev.type){
dbMsgHandler.error(ev);
return;
}
T.assert(f instanceof Function);
f(ev);
return;
}
switch(ev.type){
case 'sqlite3-api':
switch(ev.result){
case 'worker1-ready':
log("Message:",ev);
self.sqlite3TestModule.setStatus(null);
runTests();
return;
default:
warn("Unknown sqlite3-api message type:",ev);
return;
}
default:
if(dbMsgHandler.hasOwnProperty(ev.type)){
try{dbMsgHandler[ev.type](ev);}
catch(err){
error("Exception while handling db result message",
ev,":",err);
}
return;
}
warn("Unknown sqlite3-api message type:",ev);
}
};
log("Init complete, but async init bits may still be running.");
log("Installing Worker into global scope SW for dev purposes.");
self.SW = SW;
})();