mirror of
https://github.com/sqlite/sqlite.git
synced 2025-07-30 19:03:16 +03:00
Expand JS tests for db export/import and document reason it cannot currently work with kvvfs. Fix a minor JS build dependencies bug. Update page title with PASS/FAIL prefix for tester1.js to improve overview when launching multiple test tabs. Add ability of tester1 should-run-test predicates to report why a given test is disabled.
FossilOrigin-Name: 75f610d3a4cf3d972220f9abc27cdf5990451e3835ceb9cf66973934004dfc5c
This commit is contained in:
@ -569,7 +569,7 @@ sqlite3-wasm.c := $(dir.api)/sqlite3-wasm.c
|
||||
# instead of building a shared copy of sqlite3-wasm.o.
|
||||
$(eval $(call call-make-pre-js,sqlite3,vanilla))
|
||||
$(eval $(call call-make-pre-js,sqlite3,esm))
|
||||
$(sqlite3.js) $(sqlite3.mjs): $(MAKEFILE) $(sqlite3.wasm.obj) \
|
||||
$(sqlite3.js) $(sqlite3.mjs): $(MAKEFILE) $(sqlite3-wasm.c) \
|
||||
$(EXPORTED_FUNCTIONS.api)
|
||||
$(sqlite3.js): $(pre-post-sqlite3.deps.vanilla)
|
||||
$(sqlite3.mjs): $(pre-post-sqlite3.deps.esm)
|
||||
|
@ -1368,13 +1368,15 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
|
||||
- "memdb": results are undefined.
|
||||
|
||||
- "kvvfs": results are undefined.
|
||||
- "kvvfs": will fail with an I/O error due to strict internal
|
||||
requirments of that VFS's xTruncate().
|
||||
|
||||
- "unix" and related: will use the WASM build's equivalent of the
|
||||
POSIX I/O APIs.
|
||||
POSIX I/O APIs. This will work so long as neither a specific
|
||||
VFS nor the WASM environment imposes requirements which break it.
|
||||
|
||||
- "opfs": if available, uses OPFS storage and _does_ create
|
||||
directory parts of the filename.
|
||||
- "opfs": uses OPFS storage and creates directory parts of the
|
||||
filename.
|
||||
*/
|
||||
capi.sqlite3_js_vfs_create_file = function(vfs, filename, data, dataLen){
|
||||
let pData;
|
||||
|
@ -950,14 +950,18 @@ int sqlite3_wasm_db_serialize( sqlite3 *pDb, const char *zSchema,
|
||||
** for use by the sqlite project's own JS/WASM bindings.
|
||||
**
|
||||
** Creates a new file using the I/O API of the given VFS, containing
|
||||
** the given number of bytes of the given data. If the file exists,
|
||||
** it is truncated to the given length and populated with the given
|
||||
** the given number of bytes of the given data. If the file exists, it
|
||||
** is truncated to the given length and populated with the given
|
||||
** data.
|
||||
**
|
||||
** This function exists so that we can implement the equivalent of
|
||||
** Emscripten's FS.createDataFile() in a VFS-agnostic way. This
|
||||
** functionality is intended for use in uploading database files.
|
||||
**
|
||||
** Note that not all VFSes support this operation because they impose
|
||||
** specific requirements on truncate and write sizes. e.g. kvvfs does
|
||||
** not work with this.
|
||||
**
|
||||
** If pVfs is NULL, sqlite3_vfs_find(0) is used.
|
||||
**
|
||||
** If zFile is NULL, pVfs is NULL (and sqlite3_vfs_find(0) returns
|
||||
@ -1004,11 +1008,17 @@ int sqlite3_wasm_vfs_create_file( sqlite3_vfs *pVfs,
|
||||
** it may have a buffer limit related to sqlite3's pager size, we
|
||||
** conservatively write in 512-byte blocks (smallest page
|
||||
** size). */;
|
||||
|
||||
//fprintf(stderr, "pVfs=%p, zFilename=%s, nData=%d\n", pVfs, zFilename, nData);
|
||||
if( !pVfs ) pVfs = sqlite3_vfs_find(0);
|
||||
if( !pVfs || !zFilename || nData<0 ) return SQLITE_MISUSE;
|
||||
pVfs->xAccess(pVfs, zFilename, SQLITE_ACCESS_EXISTS, &fileExisted);
|
||||
rc = sqlite3OsOpenMalloc(pVfs, zFilename, &pFile, openFlags, &flagsOut);
|
||||
#if 0
|
||||
# define RC fprintf(stderr,"create_file(%s,%s) @%d rc=%d\n", pVfs->zName, zFilename, __LINE__, rc);
|
||||
#else
|
||||
# define RC
|
||||
#endif
|
||||
RC;
|
||||
if(rc) return rc;
|
||||
pIo = pFile->pMethods;
|
||||
if( pIo->xLock ) {
|
||||
@ -1019,19 +1029,25 @@ int sqlite3_wasm_vfs_create_file( sqlite3_vfs *pVfs,
|
||||
** xFileSize(), xTruncate(), and the like, and release the lock
|
||||
** only if it was unlocked when the op was started. */
|
||||
rc = pIo->xLock(pFile, SQLITE_LOCK_EXCLUSIVE);
|
||||
RC;
|
||||
doUnlock = 0==rc;
|
||||
}
|
||||
if( 0==rc) rc = pIo->xTruncate(pFile, nData);
|
||||
if( 0==rc ){
|
||||
rc = pIo->xTruncate(pFile, nData);
|
||||
RC;
|
||||
}
|
||||
if( 0==rc && 0!=pData && nData>0 ){
|
||||
while( 0==rc && nData>0 ){
|
||||
const int n = nData>=blockSize ? blockSize : nData;
|
||||
rc = pIo->xWrite(pFile, pPos, n, (sqlite3_int64)(pPos - pData));
|
||||
RC;
|
||||
nData -= n;
|
||||
pPos += n;
|
||||
}
|
||||
if( 0==rc && nData>0 ){
|
||||
assert( nData<blockSize );
|
||||
rc = pIo->xWrite(pFile, pPos, nData, (sqlite3_int64)(pPos - pData));
|
||||
RC;
|
||||
}
|
||||
}
|
||||
if( pIo->xUnlock && doUnlock!=0 ) pIo->xUnlock(pFile, SQLITE_LOCK_NONE);
|
||||
@ -1039,6 +1055,8 @@ int sqlite3_wasm_vfs_create_file( sqlite3_vfs *pVfs,
|
||||
if( rc!=0 && 0==fileExisted ){
|
||||
pVfs->xDelete(pVfs, zFilename, 1);
|
||||
}
|
||||
RC;
|
||||
#undef RC
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -62,11 +62,16 @@
|
||||
case 'error':
|
||||
logHtml('error', ...data.payload.args);
|
||||
break;
|
||||
case 'test-result':
|
||||
document.querySelector('#color-target').classList.add(
|
||||
data.payload.pass ? 'tests-pass' : 'tests-fail'
|
||||
);
|
||||
break;
|
||||
case 'test-result':{
|
||||
document.querySelector('#color-target').classList.add(
|
||||
data.payload.pass ? 'tests-pass' : 'tests-fail'
|
||||
);
|
||||
const e = document.querySelector('title');
|
||||
e.innerText = (
|
||||
data.payload.pass ? 'PASS' : 'FAIL'
|
||||
) + ': ' + e.innerText;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
logHtml('error',"Unhandled message:",data.type);
|
||||
};
|
||||
|
@ -118,8 +118,10 @@ self.sqlite3InitModule = sqlite3InitModule;
|
||||
}
|
||||
const reportFinalTestStatus = function(pass){
|
||||
if(isUIThread()){
|
||||
const e = document.querySelector('#color-target');
|
||||
let e = document.querySelector('#color-target');
|
||||
e.classList.add(pass ? 'tests-pass' : 'tests-fail');
|
||||
e = document.querySelector('title');
|
||||
e.innerText = (pass ? 'PASS' : 'FAIL') + ': ' + e.innerText;
|
||||
}else{
|
||||
postMessage({type:'test-result', payload:{pass}});
|
||||
}
|
||||
@ -246,10 +248,13 @@ self.sqlite3InitModule = sqlite3InitModule;
|
||||
log(TestUtil.separator);
|
||||
logClass('group-start',"Group #"+this.number+':',this.name);
|
||||
const indent = ' ';
|
||||
if(this.predicate && !this.predicate(sqlite3)){
|
||||
logClass('warning',indent,
|
||||
"SKIPPING group because predicate says to.");
|
||||
return;
|
||||
if(this.predicate){
|
||||
const p = this.predicate(sqlite3);
|
||||
if(!p || 'string'===typeof p){
|
||||
logClass('warning',indent,
|
||||
"SKIPPING group:", p ? p : "predicate says to" );
|
||||
return;
|
||||
}
|
||||
}
|
||||
const assertCount = TestUtil.counter;
|
||||
const groupState = Object.create(null);
|
||||
@ -259,24 +264,27 @@ self.sqlite3InitModule = sqlite3InitModule;
|
||||
++i;
|
||||
const n = this.number+"."+i;
|
||||
log(indent, n+":", t.name);
|
||||
if(t.predicate && !t.predicate(sqlite3)){
|
||||
logClass('warning', indent, indent,
|
||||
'SKIPPING because predicate says to');
|
||||
skipped.push( n+': '+t.name );
|
||||
}else{
|
||||
const tc = TestUtil.counter, now = performance.now();
|
||||
await t.test.call(groupState, sqlite3);
|
||||
const then = performance.now();
|
||||
runtime += then - now;
|
||||
logClass('faded',indent, indent,
|
||||
TestUtil.counter - tc, 'assertion(s) in',
|
||||
roundMs(then-now),'ms');
|
||||
if(t.predicate){
|
||||
const p = t.predicate(sqlite3);
|
||||
if(!p || 'string'===typeof p){
|
||||
logClass('warning',indent,
|
||||
"SKIPPING:", p ? p : "predicate says to" );
|
||||
skipped.push( n+': '+t.name );
|
||||
continue;
|
||||
}
|
||||
}
|
||||
const tc = TestUtil.counter, now = performance.now();
|
||||
await t.test.call(groupState, sqlite3);
|
||||
const then = performance.now();
|
||||
runtime += then - now;
|
||||
logClass('faded',indent, indent,
|
||||
TestUtil.counter - tc, 'assertion(s) in',
|
||||
roundMs(then-now),'ms');
|
||||
}
|
||||
logClass('green',
|
||||
"Group #"+this.number+":",(TestUtil.counter - assertCount),
|
||||
"assertion(s) in",roundMs(runtime),"ms");
|
||||
if(skipped.length){
|
||||
if(0 && skipped.length){
|
||||
logClass('warning',"SKIPPED test(s) in group",this.number+":",skipped);
|
||||
}
|
||||
}
|
||||
@ -1380,13 +1388,38 @@ self.sqlite3InitModule = sqlite3InitModule;
|
||||
})
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
.t('sqlite3_js_db_export()', function(){
|
||||
const db = this.db;
|
||||
const xp = capi.sqlite3_js_db_export(db.pointer);
|
||||
T.assert(xp instanceof Uint8Array)
|
||||
.assert(xp.byteLength>0)
|
||||
.assert(0 === xp.byteLength % 512);
|
||||
.t({
|
||||
name: 'sqlite3_js_db_export()',
|
||||
predicate: ()=>true,
|
||||
test: function(sqlite3){
|
||||
const db = this.db;
|
||||
const xp = capi.sqlite3_js_db_export(db.pointer);
|
||||
T.assert(xp instanceof Uint8Array)
|
||||
.assert(xp.byteLength>0)
|
||||
.assert(0 === xp.byteLength % 512);
|
||||
this.dbExport = xp;
|
||||
}
|
||||
}/*sqlite3_js_db_export()*/)
|
||||
.t({
|
||||
name: 'sqlite3_js_vfs_create_file() with db in default VFS',
|
||||
predicate: ()=>true,
|
||||
test: function(sqlite3){
|
||||
const db = this.db;
|
||||
const pVfs = capi.sqlite3_js_db_vfs(db);
|
||||
const filename = "sqlite3_js_vfs_create_file().db";
|
||||
capi.sqlite3_js_vfs_create_file(pVfs, filename, this.dbExport);
|
||||
delete this.dbExport;
|
||||
const db2 = new sqlite3.oo1.DB(filename,'r');
|
||||
try {
|
||||
const sql = "select count(*) from t";
|
||||
const n = db.selectValue(sql);
|
||||
T.assert(n>0 && db2.selectValue(sql) === n);
|
||||
}finally{
|
||||
if(db2) db2.close();
|
||||
wasm.sqlite3_wasm_vfs_unlink(pVfs, filename);
|
||||
}
|
||||
}
|
||||
}/*sqlite3_js_vfs_create_file()*/)
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
.t('Scalar UDFs', function(sqlite3){
|
||||
@ -1757,53 +1790,70 @@ self.sqlite3InitModule = sqlite3InitModule;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
T.g('kvvfs')
|
||||
.t('kvvfs sanity checks', function(sqlite3){
|
||||
if(isWorker()){
|
||||
.t({
|
||||
name: 'kvvfs is disabled in worker',
|
||||
predicate: ()=>(isWorker() || "test is only valid in a Worker"),
|
||||
test: function(sqlite3){
|
||||
T.assert(
|
||||
!capi.sqlite3_vfs_find('kvvfs'),
|
||||
"Expecting kvvfs to be unregistered."
|
||||
);
|
||||
log("kvvfs is (correctly) unavailable in a Worker.");
|
||||
return;
|
||||
}
|
||||
const filename = 'session';
|
||||
const pVfs = capi.sqlite3_vfs_find('kvvfs');
|
||||
T.assert(pVfs);
|
||||
const JDb = sqlite3.oo1.JsStorageDb;
|
||||
const unlink = ()=>JDb.clearStorage(filename);
|
||||
unlink();
|
||||
let db = new JDb(filename);
|
||||
try {
|
||||
db.exec([
|
||||
'create table kvvfs(a);',
|
||||
'insert into kvvfs(a) values(1),(2),(3)'
|
||||
]);
|
||||
T.assert(3 === db.selectValue('select count(*) from kvvfs'));
|
||||
db.close();
|
||||
db = new JDb(filename);
|
||||
db.exec('insert into kvvfs(a) values(4),(5),(6)');
|
||||
T.assert(6 === db.selectValue('select count(*) from kvvfs'));
|
||||
|
||||
// Check import/export of db...
|
||||
if(0){
|
||||
// does not yet work with kvvfs for unknown reasons...
|
||||
const exp = capi.sqlite3_js_db_export(db);
|
||||
db.close();
|
||||
unlink();
|
||||
capi.sqlite3_js_vfs_create_file("kvvfs", filename, exp);
|
||||
db = new JDb(filename);
|
||||
T.assert(6 === db.selectValue('select count(*) from kvvfs'));
|
||||
}
|
||||
}finally{
|
||||
db.close();
|
||||
})
|
||||
.t({
|
||||
name: 'kvvfs in main thread',
|
||||
predicate: ()=>(isUIThread() ? true : "No local/sessionStorage in Worker"),
|
||||
test: function(sqlite3){
|
||||
const filename = this.kvvfsDbFile = 'session';
|
||||
const pVfs = capi.sqlite3_vfs_find('kvvfs');
|
||||
T.assert(pVfs);
|
||||
const JDb = this.JDb = sqlite3.oo1.JsStorageDb;
|
||||
const unlink = this.kvvfsUnlink = ()=>{JDb.clearStorage(filename)};
|
||||
unlink();
|
||||
let db = new JDb(filename);
|
||||
try {
|
||||
db.exec([
|
||||
'create table kvvfs(a);',
|
||||
'insert into kvvfs(a) values(1),(2),(3)'
|
||||
]);
|
||||
T.assert(3 === db.selectValue('select count(*) from kvvfs'));
|
||||
db.close();
|
||||
db = new JDb(filename);
|
||||
db.exec('insert into kvvfs(a) values(4),(5),(6)');
|
||||
T.assert(6 === db.selectValue('select count(*) from kvvfs'));
|
||||
}finally{
|
||||
db.close();
|
||||
}
|
||||
}
|
||||
}/*kvvfs sanity checks*/)
|
||||
.t({
|
||||
name: 'kvvfs sqlite3_js_vfs_create_file()',
|
||||
predicate: ()=>"kvvfs does not currently support this",
|
||||
test: function(sqlite3){
|
||||
let db;
|
||||
try {
|
||||
db = new this.JDb(this.kvvfsDbFile);
|
||||
const exp = capi.sqlite3_js_db_export(db);
|
||||
db.close();
|
||||
this.kvvfsUnlink();
|
||||
capi.sqlite3_js_vfs_create_file("kvvfs", this.kvvfsDbFile, exp);
|
||||
db = new this.JDb(filename);
|
||||
T.assert(6 === db.selectValue('select count(*) from kvvfs'));
|
||||
}finally{
|
||||
db.close();
|
||||
this.kvvfsUnlink();
|
||||
}
|
||||
delete this.kvvfsDbFile;
|
||||
delete this.kvvfsUnlink;
|
||||
delete this.JDb;
|
||||
}
|
||||
}/*kvvfs sqlite3_js_vfs_create_file()*/)
|
||||
;/* end kvvfs tests */
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
T.g('OPFS (Worker thread only and only in supported browsers)',
|
||||
(sqlite3)=>!!sqlite3.opfs)
|
||||
T.g('OPFS: Origin-Private File System',
|
||||
(sqlite3)=>(sqlite3.opfs
|
||||
? true : "requires Worker thread in a compatible browser"))
|
||||
.t({
|
||||
name: 'OPFS db sanity checks',
|
||||
test: async function(sqlite3){
|
||||
|
Reference in New Issue
Block a user