1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-30 19:03:16 +03:00

Add initial infrastructure for setting up function/regression tests for the JS/WASM APIs.

FossilOrigin-Name: 7f5db9829b6e60fadb756fea5442da1f4368c8428bb5cdaf14f97f0c3c8451d9
This commit is contained in:
stephan
2022-10-12 15:54:28 +00:00
parent b75971e6e9
commit 402da26c16
5 changed files with 267 additions and 6 deletions

View File

@ -0,0 +1,37 @@
<!doctype html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon">
<link rel="stylesheet" href="../common/emscripten.css"/>
<link rel="stylesheet" href="../common/testing.css"/>
<title>sqlite3 tester #1 (Worker thread)</title>
<style>
body {
font-family: monospace;
}
</style>
</head>
<body>
<h1>sqlite3 WASM/JS tester #1 (Worker thread)</h1>
<script>(function(){
const logHtml = function(cssClass,...args){
const ln = document.createElement('div');
if(cssClass) ln.classList.add(cssClass);
ln.append(document.createTextNode(args.join(' ')));
document.body.append(ln);
};
const w = new Worker("tester1.js");
w.onmessage = function({data}){
switch(data.type){
case 'log':
logHtml(data.payload.cssClass, ...data.payload.args);
break;
default:
logHtml('error',"Unhandled message:",data.type);
};
};
})();</script>
</body>
</html>

21
ext/wasm/tester1.html Normal file
View File

@ -0,0 +1,21 @@
<!doctype html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon">
<link rel="stylesheet" href="../common/emscripten.css"/>
<link rel="stylesheet" href="../common/testing.css"/>
<title>sqlite3 tester #1 (UI thread)</title>
<style>
body {
font-family: monospace;
}
</style>
</head>
<body>
<h1>sqlite3 WASM/JS tester #1 (UI thread)</h1>
<script src="sqlite3.js"></script>
<script src="tester1.js"></script>
</body>
</html>

200
ext/wasm/tester1.js Normal file
View File

@ -0,0 +1,200 @@
/*
2022-10-12
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.
***********************************************************************
Main functional and regression tests for the sqlite3 WASM API.
*/
'use strict';
(function(){
/**
Set up our output channel differently depending
on whether we are running in a worker thread or
the main (UI) thread.
*/
let logHtml;
const isUIThread = ()=>(self.window===self && self.document);
const mapToString = (v)=>{
switch(typeof v){
case 'number': case 'string': case 'boolean':
case 'undefined':
return ''+v;
default: break;
}
if(null===v) return 'null';
return JSON.stringify(v,undefined,2);
};
const normalizeArgs = (args)=>args.map(mapToString);
if( isUIThread() ){
console.log("Running UI thread.");
logHtml = function(cssClass,...args){
const ln = document.createElement('div');
if(cssClass) ln.classList.add(cssClass);
ln.append(document.createTextNode(normalizeArgs(args).join(' ')));
document.body.append(ln);
};
}else{ /* Worker thread */
console.log("Running Worker thread.");
logHtml = function(cssClass,...args){
postMessage({
type:'log',
payload:{cssClass, args: normalizeArgs(args)}
});
};
}
const log = (...args)=>{
//console.log(...args);
logHtml('',...args);
}
const warn = (...args)=>{
console.warn(...args);
logHtml('warning',...args);
}
const error = (...args)=>{
console.error(...args);
logHtml('error',...args);
};
const toss = (...args)=>{
error(...args);
throw new Error(args.join(' '));
};
/**
Helpers for writing sqlite3-specific tests.
*/
const TestUtil = {
/** Running total of the number of tests run via
this API. */
counter: 0,
/**
If expr is a function, it is called and its result
is returned, coerced to a bool, else expr, coerced to
a bool, is returned.
*/
toBool: function(expr){
return (expr instanceof Function) ? !!expr() : !!expr;
},
/** abort() if expr is false. If expr is a function, it
is called and its result is evaluated.
*/
assert: function f(expr, msg){
if(!f._){
f._ = ('undefined'===typeof abort
? (msg)=>{throw new Error(msg)}
: abort);
}
++this.counter;
if(!this.toBool(expr)){
f._(msg || "Assertion failed.");
}
return this;
},
/** Identical to assert() but throws instead of calling
abort(). */
affirm: function(expr, msg){
++this.counter;
if(!this.toBool(expr)) throw new Error(msg || "Affirmation failed.");
return this;
},
/** Calls f() and squelches any exception it throws. If it
does not throw, this function throws. */
mustThrow: function(f, msg){
++this.counter;
let err;
try{ f(); } catch(e){err=e;}
if(!err) throw new Error(msg || "Expected exception.");
return this;
},
/**
Works like mustThrow() but expects filter to be a regex,
function, or string to match/filter the resulting exception
against. If f() does not throw, this test fails and an Error is
thrown. If filter is a regex, the test passes if
filter.test(error.message) passes. If it's a function, the test
passes if filter(error) returns truthy. If it's a string, the
test passes if the filter matches the exception message
precisely. In all other cases the test fails, throwing an
Error.
If it throws, msg is used as the error report unless it's falsy,
in which case a default is used.
*/
mustThrowMatching: function(f, filter, msg){
++this.counter;
let err;
try{ f(); } catch(e){err=e;}
if(!err) throw new Error(msg || "Expected exception.");
let pass = false;
if(filter instanceof RegExp) pass = filter.test(err.message);
else if(filter instanceof Function) pass = filter(err);
else if('string' === typeof filter) pass = (err.message === filter);
if(!pass){
throw new Error(msg || ("Filter rejected this exception: "+err.message));
}
return this;
},
/** Throws if expr is truthy or expr is a function and expr()
returns truthy. */
throwIf: function(expr, msg){
++this.counter;
if(this.toBool(expr)) throw new Error(msg || "throwIf() failed");
return this;
},
/** Throws if expr is falsy or expr is a function and expr()
returns falsy. */
throwUnless: function(expr, msg){
++this.counter;
if(!this.toBool(expr)) throw new Error(msg || "throwUnless() failed");
return this;
},
TestGroup: class {
constructor(name){
this.name = name;
this.tests = [];
}
push(name,callback){
}
}/*TestGroup*/,
testGroups: [],
currentTestGroup: undefined,
addGroup: function(name){
if(this.testGroups[name]){
toss("Test group already exists:",name);
}
this.testGroups.push( this.currentTestGroup = new this.TestGroup(name) );
return this;
},
addTest: function(name, callback){
this.currentTestGroup.push(name, callback);
},
runTests: function(){
toss("TODO: runTests()");
}
}/*TestUtil*/;
log("Loading and initializing sqlite3 WASM module...");
if(!isUIThread()){
importScripts("sqlite3.js");
}
self.sqlite3InitModule({
print: log,
printErr: error
}).then(function(sqlite3){
//console.log('sqlite3 =',sqlite3);
log("Done initializing. Running tests...");
try {
TestUtil.runTests();
}catch(e){
error("Tests failed:",e.message);
}
});
})();