236 lines
8.3 KiB
JavaScript
236 lines
8.3 KiB
JavaScript
/*
|
|
2022-05-22
|
|
|
|
The author disclaims copyright to this source code. In place of a
|
|
legal notice, here is a blessing:
|
|
|
|
* May you do good and not evil.
|
|
* May you find forgiveness for yourself and forgive others.
|
|
* May you share freely, never taking more than you give.
|
|
|
|
***********************************************************************
|
|
|
|
A basic test script for sqlite3-worker.js.
|
|
*/
|
|
(function(){
|
|
const T = self.SqliteTestUtil;
|
|
const SW = new Worker("sqlite3-worker.js");
|
|
/** Posts a worker message as {type:type, data:data}. */
|
|
const wMsg = function(type,data){
|
|
SW.postMessage({type, data});
|
|
return SW;
|
|
};
|
|
const log = console.log.bind(console);
|
|
const warn = console.warn.bind(console);
|
|
const error = console.error.bind(console);
|
|
|
|
SW.onerror = function(event){
|
|
error("onerror",event);
|
|
};
|
|
|
|
/**
|
|
A queue for callbacks which are to be run in response to async
|
|
DB commands. See the notes in runTests() for why we need
|
|
this. The event-handling plumbing of this file requires that
|
|
any DB command which includes a `messageId` property also have
|
|
a queued callback entry, as the existence of that property in
|
|
response payloads is how it knows whether or not to shift an
|
|
entry off of the queue.
|
|
*/
|
|
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 = ()=>log("Total test count:",T.counter);
|
|
|
|
const runOneTest = function(eventType, eventData, callback){
|
|
T.assert(eventData && 'object'===typeof eventData);
|
|
/* ^^^ that is for the testing and messageId-related code, not
|
|
a hard requirement of all of the Worker-exposed APIs. */
|
|
eventData.messageId = MsgHandlerQueue.push(eventType,function(ev){
|
|
log("runOneTest",eventType,"result",ev.data);
|
|
if(callback instanceof Function){
|
|
callback(ev);
|
|
testCount();
|
|
}
|
|
});
|
|
wMsg(eventType, eventData);
|
|
};
|
|
|
|
/** Methods which map directly to onmessage() event.type keys.
|
|
They get passed the inbound event.data. */
|
|
const dbMsgHandler = {
|
|
open: function(ev){
|
|
log("open result",ev.data);
|
|
},
|
|
exec: function(ev){
|
|
log("exec result",ev.data);
|
|
},
|
|
export: function(ev){
|
|
log("exec result",ev.data);
|
|
},
|
|
error: function(ev){
|
|
error("ERROR from the worker:",ev.data);
|
|
},
|
|
resultRowTest1: function f(ev){
|
|
if(undefined === f.counter) f.counter = 0;
|
|
if(ev.data) ++f.counter;
|
|
//log("exec() result row:",ev.data);
|
|
T.assert(null===ev.data || 'number' === typeof ev.data.b);
|
|
}
|
|
};
|
|
|
|
const runTests = function(){
|
|
const mustNotReach = ()=>{
|
|
throw new Error("This is not supposed to be reached.");
|
|
};
|
|
/**
|
|
"The problem" now is that the test results are async. We
|
|
know, however, that the messages posted to the worker will
|
|
be processed in the order they are passed to it, so we can
|
|
create a queue of callbacks to handle them. The problem
|
|
with that approach is that it's not error-handling
|
|
friendly, in that an error can cause us to bypass a result
|
|
handler queue entry. We have to perform some extra
|
|
acrobatics to account for that.
|
|
*/
|
|
runOneTest('open', {filename:'testing2.sqlite3'}, function(ev){
|
|
//log("open result",ev);
|
|
T.assert('testing2.sqlite3'===ev.data.filename)
|
|
.assert(ev.data.messageId);
|
|
});
|
|
runOneTest('exec',{
|
|
sql: ["create table t(a,b)",
|
|
"insert into t(a,b) values(1,2),(3,4),(5,6)"
|
|
].join(';'),
|
|
multi: true,
|
|
resultRows: [], columnNames: []
|
|
}, function(ev){
|
|
ev = ev.data;
|
|
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: [],
|
|
}, function(ev){
|
|
ev = ev.data;
|
|
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.data;
|
|
T.assert(3===ev.resultRows.length)
|
|
.assert(1===ev.resultRows[0].a)
|
|
.assert(6===ev.resultRows[2].b)
|
|
});
|
|
runOneTest('exec',{sql:'intentional_error'}, mustNotReach);
|
|
// Ensure that the message-handler queue survives ^^^ that error...
|
|
runOneTest('exec',{
|
|
sql:'select 1',
|
|
resultRows: [],
|
|
//rowMode: 'array', // array is the default in the Worker interface
|
|
}, function(ev){
|
|
ev = ev.data;
|
|
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: 'delete from t where a>3'});
|
|
runOneTest('exec',{
|
|
sql: 'select count(a) from t',
|
|
resultRows: []
|
|
},function(ev){
|
|
ev = ev.data;
|
|
T.assert(1===ev.resultRows.length)
|
|
.assert(2===ev.resultRows[0][0]);
|
|
});
|
|
runOneTest('export',{}, function(ev){
|
|
ev = ev.data;
|
|
T.assert('string' === typeof ev.filename)
|
|
.assert(ev.buffer instanceof Uint8Array)
|
|
.assert(ev.buffer.length > 1024)
|
|
.assert('application/x-sqlite3' === ev.mimetype);
|
|
});
|
|
|
|
/***** close() tests must come last. *****/
|
|
runOneTest('close',{unlink:true},function(ev){
|
|
ev = ev.data;
|
|
T.assert('string' === typeof ev.filename);
|
|
});
|
|
runOneTest('close',{unlink:true},function(ev){
|
|
ev = ev.data;
|
|
T.assert(undefined === ev.filename);
|
|
});
|
|
};
|
|
|
|
SW.onmessage = function(ev){
|
|
if(!ev.data || 'object'!==typeof ev.data){
|
|
warn("Unknown sqlite3-worker message type:",ev);
|
|
return;
|
|
}
|
|
ev = ev.data/*expecting a nested object*/;
|
|
//log("main window onmessage:",ev);
|
|
if(ev.data && ev.data.messageId){
|
|
/* We're expecting a queued-up callback handler. */
|
|
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.data){
|
|
case 'loaded':
|
|
log("Message:",ev); return;
|
|
case 'ready':
|
|
log("Message:",ev);
|
|
self.sqlite3TestModule.setStatus(null);
|
|
setTimeout(runTests, 0);
|
|
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.");
|
|
})();
|