Snapshot of upstream SQLite 3.46.1
[sqlcipher.git] / ext / wasm / common / SqliteTestUtil.js
blob2c17824c5336a748d4e915bf47a1b88706f2d446
1 /*
2 2022-05-22
4 The author disclaims copyright to this source code. In place of a
5 legal notice, here is a blessing:
7 * May you do good and not evil.
8 * May you find forgiveness for yourself and forgive others.
9 * May you share freely, never taking more than you give.
11 ***********************************************************************
13 This file contains bootstrapping code used by various test scripts
14 which live in this file's directory.
16 'use strict';
17 (function(self){
18 /* querySelectorAll() proxy */
19 const EAll = function(/*[element=document,] cssSelector*/){
20 return (arguments.length>1 ? arguments[0] : document)
21 .querySelectorAll(arguments[arguments.length-1]);
23 /* querySelector() proxy */
24 const E = function(/*[element=document,] cssSelector*/){
25 return (arguments.length>1 ? arguments[0] : document)
26 .querySelector(arguments[arguments.length-1]);
29 /**
30 Helpers for writing sqlite3-specific tests.
32 self.SqliteTestUtil = {
33 /** Running total of the number of tests run via
34 this API. */
35 counter: 0,
36 /**
37 If expr is a function, it is called and its result
38 is returned, coerced to a bool, else expr, coerced to
39 a bool, is returned.
41 toBool: function(expr){
42 return (expr instanceof Function) ? !!expr() : !!expr;
44 /** abort() if expr is false. If expr is a function, it
45 is called and its result is evaluated.
47 assert: function f(expr, msg){
48 if(!f._){
49 f._ = ('undefined'===typeof abort
50 ? (msg)=>{throw new Error(msg)}
51 : abort);
53 ++this.counter;
54 if(!this.toBool(expr)){
55 f._(msg || "Assertion failed.");
57 return this;
59 /** Identical to assert() but throws instead of calling
60 abort(). */
61 affirm: function(expr, msg){
62 ++this.counter;
63 if(!this.toBool(expr)) throw new Error(msg || "Affirmation failed.");
64 return this;
66 /** Calls f() and squelches any exception it throws. If it
67 does not throw, this function throws. */
68 mustThrow: function(f, msg){
69 ++this.counter;
70 let err;
71 try{ f(); } catch(e){err=e;}
72 if(!err) throw new Error(msg || "Expected exception.");
73 return this;
75 /**
76 Works like mustThrow() but expects filter to be a regex,
77 function, or string to match/filter the resulting exception
78 against. If f() does not throw, this test fails and an Error is
79 thrown. If filter is a regex, the test passes if
80 filter.test(error.message) passes. If it's a function, the test
81 passes if filter(error) returns truthy. If it's a string, the
82 test passes if the filter matches the exception message
83 precisely. In all other cases the test fails, throwing an
84 Error.
86 If it throws, msg is used as the error report unless it's falsy,
87 in which case a default is used.
89 mustThrowMatching: function(f, filter, msg){
90 ++this.counter;
91 let err;
92 try{ f(); } catch(e){err=e;}
93 if(!err) throw new Error(msg || "Expected exception.");
94 let pass = false;
95 if(filter instanceof RegExp) pass = filter.test(err.message);
96 else if(filter instanceof Function) pass = filter(err);
97 else if('string' === typeof filter) pass = (err.message === filter);
98 if(!pass){
99 throw new Error(msg || ("Filter rejected this exception: "+err.message));
101 return this;
103 /** Throws if expr is truthy or expr is a function and expr()
104 returns truthy. */
105 throwIf: function(expr, msg){
106 ++this.counter;
107 if(this.toBool(expr)) throw new Error(msg || "throwIf() failed");
108 return this;
110 /** Throws if expr is falsy or expr is a function and expr()
111 returns falsy. */
112 throwUnless: function(expr, msg){
113 ++this.counter;
114 if(!this.toBool(expr)) throw new Error(msg || "throwUnless() failed");
115 return this;
119 Parses window.location.search-style string into an object
120 containing key/value pairs of URL arguments (already
121 urldecoded). The object is created using Object.create(null),
122 so contains only parsed-out properties and has no prototype
123 (and thus no inherited properties).
125 If the str argument is not passed (arguments.length==0) then
126 window.location.search.substring(1) is used by default. If
127 neither str is passed in nor window exists then false is returned.
129 On success it returns an Object containing the key/value pairs
130 parsed from the string. Keys which have no value are treated
131 has having the boolean true value.
133 Pedantic licensing note: this code has appeared in other source
134 trees, but was originally written by the same person who pasted
135 it into those trees.
137 processUrlArgs: function(str) {
138 if( 0 === arguments.length ) {
139 if( ('undefined' === typeof window) ||
140 !window.location ||
141 !window.location.search ) return false;
142 else str = (''+window.location.search).substring(1);
144 if( ! str ) return false;
145 str = (''+str).split(/#/,2)[0]; // remove #... to avoid it being added as part of the last value.
146 const args = Object.create(null);
147 const sp = str.split(/&+/);
148 const rx = /^([^=]+)(=(.+))?/;
149 var i, m;
150 for( i in sp ) {
151 m = rx.exec( sp[i] );
152 if( ! m ) continue;
153 args[decodeURIComponent(m[1])] = (m[3] ? decodeURIComponent(m[3]) : true);
155 return args;
160 This is a module object for use with the emscripten-installed
161 sqlite3InitModule() factory function.
163 self.sqlite3TestModule = {
165 Array of functions to call after Emscripten has initialized the
166 wasm module. Each gets passed the Emscripten module object
167 (which is _this_ object).
169 postRun: [
170 /* function(theModule){...} */
172 //onRuntimeInitialized: function(){},
173 /* Proxy for C-side stdout output. */
174 print: (...args)=>{console.log(...args)},
175 /* Proxy for C-side stderr output. */
176 printErr: (...args)=>{console.error(...args)},
178 Called by the Emscripten module init bits to report loading
179 progress. It gets passed an empty argument when loading is done
180 (after onRuntimeInitialized() and any this.postRun callbacks
181 have been run).
183 setStatus: function f(text){
184 if(!f.last){
185 f.last = { text: '', step: 0 };
186 f.ui = {
187 status: E('#module-status'),
188 progress: E('#module-progress'),
189 spinner: E('#module-spinner')
192 if(text === f.last.text) return;
193 f.last.text = text;
194 if(f.ui.progress){
195 f.ui.progress.value = f.last.step;
196 f.ui.progress.max = f.last.step + 1;
198 ++f.last.step;
199 if(text) {
200 f.ui.status.classList.remove('hidden');
201 f.ui.status.innerText = text;
202 }else{
203 if(f.ui.progress){
204 f.ui.progress.remove();
205 f.ui.spinner.remove();
206 delete f.ui.progress;
207 delete f.ui.spinner;
209 f.ui.status.classList.add('hidden');
213 Config options used by the Emscripten-dependent initialization
214 which happens via this.initSqlite3(). This object gets
215 (indirectly) passed to sqlite3ApiBootstrap() to configure the
216 sqlite3 API.
218 sqlite3ApiConfig: {
219 wasmfsOpfsDir: "/opfs"
222 Intended to be called by apps which need to call the
223 Emscripten-installed sqlite3InitModule() routine. This function
224 temporarily installs this.sqlite3ApiConfig into the self
225 object, calls it sqlite3InitModule(), and removes
226 self.sqlite3ApiConfig after initialization is done. Returns the
227 promise from sqlite3InitModule(), and the next then() handler
228 will get the sqlite3 API object as its argument.
230 initSqlite3: function(){
231 self.sqlite3ApiConfig = this.sqlite3ApiConfig;
232 return self.sqlite3InitModule(this).finally(()=>delete self.sqlite3ApiConfig);
235 })(self/*window or worker*/);