]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add a UI, of sorts, to the JS SQLTester. js-tester
authorstephan <stephan@noemail.net>
Wed, 30 Aug 2023 11:54:43 +0000 (11:54 +0000)
committerstephan <stephan@noemail.net>
Wed, 30 Aug 2023 11:54:43 +0000 (11:54 +0000)
FossilOrigin-Name: 249e82b9917ea47c56ee1cbd3345a977d335fd3fc0d67a1ef157813ef4571c7c

ext/wasm/SQLTester/GNUmakefile
ext/wasm/SQLTester/SQLTester.mjs
ext/wasm/SQLTester/SQLTester.run.mjs
ext/wasm/SQLTester/index.html
manifest
manifest.uuid

index 63388084d9e3c0c454cd9b24fe531c2087cc1314..8fa1247138cea945098c724d0c82d52fa43a0846 100644 (file)
@@ -39,7 +39,7 @@ $(test-list.mjs): $(bin.touint8array) $(tests.all) $(MAKEFILE)
        @echo "Created $@"
 $(test-list.mjs.gz): $(test-list.mjs)
        gzip -c $< > $@
-CLEAAN_FILES += $(test-list.mjs.gz)
+CLEAN_FILES += $(test-list.mjs.gz)
 all: $(test-list.mjs.gz)
 else
        @echo "Cannot build $(test-list.mjs) for lack of input test files."; \
index 0ec82ca464f5978477339bdf85f42e0ed9056fdb..895c6461167b595111fb5344f7ed27f6daae7656 100644 (file)
 **
 *************************************************************************
 ** This file contains the main application entry pointer for the JS
-** implementation of the SQLTester framework. This version is not well
-** documented because the one it's a direct port of is documented:
-** in the main SQLite3 source tree, see
-** ext/jni/src/org/sqlite/jni/tester/SQLite3Tester.java.
+** implementation of the SQLTester framework.
+**
+** This version is not well-documented because it's a direct port of
+** the Java immplementation, which is documented: in the main SQLite3
+** source tree, see ext/jni/src/org/sqlite/jni/tester/SQLite3Tester.java.
 */
 
 import sqlite3ApiInit from '/jswasm/sqlite3.mjs';
@@ -24,6 +25,12 @@ const log = (...args)=>{
   console.log('SQLTester:',...args);
 };
 
+/**
+   Try to install vfsName as the new default VFS. Once this succeeds
+   (returns true) then it becomes a no-op on future calls. Throws if
+   vfs registration as the default VFS fails but has no side effects
+   if vfsName is not currently registered.
+*/
 const tryInstallVfs = function f(vfsName){
   if(f.vfsName) return false;
   const pVfs = sqlite3.capi.sqlite3_vfs_find(vfsName);
@@ -39,9 +46,13 @@ const tryInstallVfs = function f(vfsName){
 };
 tryInstallVfs.vfsName = undefined;
 
-if( 1 ){
+if( 0 && globalThis.WorkerGlobalScope ){
   // Try OPFS storage, if available...
-  if(sqlite3.installOpfsSAHPoolVfs){
+  if( 0 && sqlite3.oo1.OpfsDb ){
+    /* Really slow with these tests */
+    tryInstallVfs("opfs");
+  }
+  if( sqlite3.installOpfsSAHPoolVfs ){
     await sqlite3.installOpfsSAHPoolVfs({
       clearOnInit: true,
       initialCapacity: 15,
@@ -52,14 +63,16 @@ if( 1 ){
       log("OpfsSAHPool could not load:",e);
     });
   }
-  if(sqlite3.oo1.OpfsDb){
-    tryInstallVfs("opfs");
-  }
 }
 
-const wPost = (type,...payload)=>{
-  postMessage({type, payload});
-};
+const wPost = (function(){
+  return (('undefined'===typeof WorkerGlobalScope)
+          ? ()=>{}
+          : (type, payload)=>{
+            postMessage({type, payload});
+          });
+})();
+//log("WorkerGlobalScope",globalThis.WorkerGlobalScope);
 
 // Return a new enum entry value
 const newE = ()=>Object.create(null);
@@ -99,6 +112,12 @@ class SQLTesterException extends globalThis.Error {
     }
     this.name = 'SQLTesterException';
   }
+  /**
+     If this overrideable method returns false (the default) then
+     exceptions of that type are fatal to a whole test run, instead of
+     just the test which triggered it. If the the "keep going" flag
+     is set, this preference is ignored.
+  */
   isFatal() { return false; }
 }
 
@@ -107,7 +126,7 @@ SQLTesterException.toss = (...args)=>{
 }
 
 class DbException extends SQLTesterException {
-  constructor(testScript, pDb, rc, closeDb){
+  constructor(testScript, pDb, rc, closeDb=false){
     super(testScript, "DB error #"+rc+": "+sqlite3.capi.sqlite3_errmsg(pDb));
     this.name = 'DbException';
     if( closeDb ) sqlite3.capi.sqlite3_close_v2(pDb);
@@ -137,12 +156,14 @@ class IncompatibleDirective extends SQLTesterException {
   }
 }
 
+//! For throwing where an expression is required.
 const toss = (errType, ...args)=>{
   throw new errType(...args);
 };
 
 const __utf8Decoder = new TextDecoder();
 const __utf8Encoder = new TextEncoder('utf-8');
+//! Workaround for Util.utf8Decode()
 const __SAB = ('undefined'===typeof globalThis.SharedArrayBuffer)
       ? function(){} : globalThis.SharedArrayBuffer;
 
@@ -190,26 +211,41 @@ class Outer {
   #verbosity = 0;
   #logger = console.log.bind(console);
 
-  constructor(){
+  constructor(func){
+    if(func) this.setFunc(func);
+  }
+
+  logger(...args){
+    if(args.length){
+      this.#logger = args[0];
+      return this;
+    }
+    return this.#logger;
   }
 
   out(...args){
-    if(!this.#lnBuf.length && this.getOutputPrefix ){
+    if( this.getOutputPrefix && !this.#lnBuf.length ){
       this.#lnBuf.push(this.getOutputPrefix());
     }
     this.#lnBuf.push(...args);
     return this;
   }
-  outln(...args){
-    if(!this.#lnBuf.length && this.getOutputPrefix ){
+
+  #outlnImpl(vLevel, ...args){
+    if( this.getOutputPrefix && !this.#lnBuf.length ){
       this.#lnBuf.push(this.getOutputPrefix());
     }
     this.#lnBuf.push(...args,'\n');
-    this.#logger(this.#lnBuf.join(''));
+    const msg = this.#lnBuf.join('');
     this.#lnBuf.length = 0;
+    this.#logger(msg);
     return this;
   }
 
+  outln(...args){
+    return this.#outlnImpl(0,...args);
+  }
+
   outputPrefix(){
     if( 0==arguments.length ){
       return (this.getOutputPrefix
@@ -220,9 +256,9 @@ class Outer {
     }
   }
 
-  verboseN(lvl, argv){
+  verboseN(lvl, args){
     if( this.#verbosity>=lvl ){
-      this.outln('VERBOSE ',lvl,': ',...argv);
+      this.#outlnImpl(lvl,'VERBOSE ',lvl,': ',...args);
     }
   }
   verbose1(...args){ return this.verboseN(1,args); }
@@ -230,13 +266,8 @@ class Outer {
   verbose3(...args){ return this.verboseN(3,args); }
 
   verbosity(){
-    let rc;
-    if(arguments.length){
-      rc = this.#verbosity;
-      this.#verbosity = arguments[0];
-    }else{
-      rc = this.#verbosity;
-    }
+    const rc = this.#verbosity;
+    if(arguments.length) this.#verbosity = +arguments[0];
     return rc;
   }
 
@@ -261,7 +292,7 @@ class SQLTester {
     nTestFile: 0,
     //! Number of scripts which were aborted
     nAbortedScript: 0,
-    //! Incremented by test case handlers
+    //! Test-case count for to the current TestScript
     nTest: 0
   });
   #emitColNames = false;
@@ -288,6 +319,24 @@ class SQLTester {
 
   outln(...args){ return this.#outer.outln(...args); }
   out(...args){ return this.#outer.out(...args); }
+  outer(...args){
+    if(args.length){
+      this.#outer = args[0];
+      return this;
+    }
+    return this.#outer;
+  }
+  verbose1(...args){ return this.#outer.verboseN(1,args); }
+  verbose2(...args){ return this.#outer.verboseN(2,args); }
+  verbose3(...args){ return this.#outer.verboseN(3,args); }
+  verbosity(...args){
+    const rc = this.#outer.verbosity(...args);
+    return args.length ? this : rc;
+  }
+  setLogger(func){
+    this.#outer.logger(func);
+    return this;
+  }
 
   incrementTestCounter(){
     ++this.metrics.nTotalTest;
@@ -339,8 +388,6 @@ class SQLTester {
     return this.#takeBuffer(this.#resultBuffer);
   }
 
-  verbosity(...args){ return this.#outer.verbosity(...args); }
-
   nullValue(){
     if( 0==arguments.length ){
       return this.#nullView;
@@ -427,7 +474,7 @@ class SQLTester {
         pDb = wasm.peekPtr(ppOut);
       });
       if( 0==rc && this.#db.initSql.length > 0){
-        //this.#outer.verbose2("RUNNING DB INIT CODE: ",this.#db.initSql.toString());
+        this.#outer.verbose2("RUNNING DB INIT CODE: ",this.#db.initSql.toString());
         rc = this.execSql(pDb, false, ResultBufferMode.NONE,
                           null, this.#db.initSql.join(''));
       }
@@ -464,6 +511,8 @@ class SQLTester {
   runTests(){
     const tStart = (new Date()).getTime();
     let isVerbose = this.verbosity();
+    this.metrics.nAbortedScript = 0;
+    this.metrics.nTotalTest = 0;
     for(const ts of this.#aScripts){
       this.reset();
       ++this.metrics.nTestFile;
@@ -471,11 +520,6 @@ class SQLTester {
       const timeStart = (new Date()).getTime();
       let msgTail = '';
       try{
-        if( isVerbose ){
-          this.#outer.verbose1("Running ",ts.filename());
-        }else{
-          msgTail = ' '+ts.filename();
-        }
         ts.run(this);
       }catch(e){
         if(e instanceof SQLTesterException){
@@ -483,21 +527,35 @@ class SQLTester {
           this.outln("🔥EXCEPTION: ",e);
           ++this.metrics.nAbortedScript;
           if( this.#keepGoing ){
-            this.outln("Continuing anyway becaure of the keep-going option.");
+            this.outln("Continuing anyway because of the keep-going option.");
+          }else if( e.isFatal() ){
+            throw e;
           }
-          else if( e.isFatal() ) throw e;
         }else{
           throw e;
         }
       }finally{
         const timeEnd = (new Date()).getTime();
-        this.outln("🏁", (threw ? "❌" : "✅"), " ", this.metrics.nTest,
-                   " test(s) in ", (timeEnd-timeStart),"ms.",msgTail);
+        this.out("🏁", (threw ? "❌" : "✅"), " ",
+                 this.metrics.nTest, " test(s) in ",
+                 (timeEnd-timeStart),"ms. ");
+        const mod = ts.moduleName();
+        if( mod ){
+          this.out( "[",mod,"] " );
+        }
+        this.outln(ts.filename());
       }
     }
     const tEnd = (new Date()).getTime();
-    this.outln("Total run-time: ",(tEnd-tStart),"ms");
     Util.unlink(this.#db.initialDbName);
+    this.outln("Took ",(tEnd-tStart),"ms. test count = ",
+               this.metrics.nTotalTest,", script count = ",
+               this.#aScripts.length,(
+                 this.metrics.nAbortedScript
+                   ? ", aborted scripts = "+this.metrics.nAbortedScript
+                   : ""
+               )
+              );
     return this;
   }
 
@@ -511,10 +569,6 @@ class SQLTester {
     }
   }
 
-  /**
-     Returns v or some escaped form of v, as defined in the tester's
-     spec doc.
-  */
   #escapeSqlValue(v){
     if( !v ) return "{}";
     if( !Rx.special.test(v) ){
@@ -735,6 +789,11 @@ class TestScript {
     this.#cursor.src = content;
   }
 
+  moduleName(){
+    return (0==arguments.length)
+      ? this.#moduleName : (this.#moduleName = arguments[0]);
+  }
+
   testCaseName(){
     return (0==arguments.length)
       ? this.#testCaseName : (this.#testCaseName = arguments[0]);
@@ -760,6 +819,14 @@ class TestScript {
     throw new TestScriptFailed(this,...args);
   }
 
+  verbose1(...args){ return this.#outer.verboseN(1,args); }
+  verbose2(...args){ return this.#outer.verboseN(2,args); }
+  verbose3(...args){ return this.#outer.verboseN(3,args); }
+  verbosity(...args){
+    const rc = this.#outer.verbosity(...args);
+    return args.length ? this : rc;
+  }
+
   #checkForDirective(tester,line){
     if(line.startsWith("#")){
       throw new IncompatibleDirective(this, "C-preprocessor input: "+line);
@@ -818,7 +885,8 @@ class TestScript {
 
   run(tester){
     this.reset();
-    this.#outer.verbosity(tester.verbosity());
+    this.#outer.verbosity( tester.verbosity() );
+    this.#outer.logger( tester.outer().logger() );
     let line, directive, argv = [];
     while( null != (line = this.getLine()) ){
       this.verbose3("run() input line: ",line);
@@ -942,11 +1010,6 @@ class TestScript {
     cur.lineNo = cur.putbackLineNo;
   }
 
-  verbose1(...args){ return this.#outer.verboseN(1,args); }
-  verbose2(...args){ return this.#outer.verboseN(2,args); }
-  verbose3(...args){ return this.#outer.verboseN(3,args); }
-  verbosity(...args){ return this.#outer.verbosity(...args); }
-
 }/*TestScript*/;
 
 //! --close command
index e6730253b6256919afa50d55dbe179b4666fbcd2..4a089045921d2c3fdc99f6b91eab6c1ea03d389d 100644 (file)
@@ -12,7 +12,7 @@
 ** This file contains a test application for SQLTester.js.
 */
 import {default as ns} from './SQLTester.mjs';
-import {default as tests} from './test-list.mjs';
+import {default as allTests} from './test-list.mjs';
 
 globalThis.sqlite3 = ns.sqlite3;
 const log = function f(...args){
@@ -33,8 +33,6 @@ const affirm = function(expr, msg){
   }
 }
 
-log("SQLTester is ready.");
-
 let ts = new ns.TestScript('/foo.test',`
 /*
 ** This is a comment. There are many like it but this one is mine.
@@ -56,10 +54,10 @@ let ts = new ns.TestScript('/foo.test',`
 --oom
 --db 0
 --new my.db
---null zilchy
+--null zilch
 --testcase 1.0
 SELECT 1, null;
---result 1 zilchy
+--result 1 zilch
 --glob *zil*
 --notglob *ZIL*
 SELECT 1, 2;
@@ -94,29 +92,54 @@ SELECT json_array(1,2,3)
 --print Until next time
 `);
 
-const sqt = new ns.SQLTester();
-try{
-  if( 0 ){
-    affirm( !sqt.getCurrentDb(), 'sqt.getCurrentDb()' );
-    sqt.openDb('/foo.db', true);
-    affirm( !!sqt.getCurrentDb(),'sqt.getCurrentDb()' );
-    sqt.verbosity(0);
-    if(false){
+const sqt = new ns.SQLTester()
+      .setLogger(console.log.bind(console))
+      .verbosity(1)
+      .addTestScript(ts);
+
+const runTests = function(){
+  try{
+    if( 0 ){
+      affirm( !sqt.getCurrentDb(), 'sqt.getCurrentDb()' );
+      sqt.openDb('/foo.db', true);
+      affirm( !!sqt.getCurrentDb(),'sqt.getCurrentDb()' );
       affirm( 'zilch' !== sqt.nullValue() );
       ts.run(sqt);
       affirm( 'zilch' === sqt.nullValue() );
+      sqt.addTestScript(ts);
+      sqt.runTests();
+    }else{
+      for(const t of allTests){
+        sqt.addTestScript( new ns.TestScript(t) );
+      }
+      allTests.length = 0;
+      sqt.runTests();
     }
-    sqt.addTestScript(ts);
-    sqt.runTests();
-  }else{
-    for(const t of tests){
-      sqt.addTestScript( new ns.TestScript(t) );
-    }
-    tests.length = 0;
-    sqt.verbosity(0);
-    sqt.runTests();
+  }finally{
+    //log( "Metrics:", sqt.metrics );
+    sqt.reset();
   }
-}finally{
-  log( "Metrics:", sqt.metrics );
-  sqt.reset();
+};
+
+if( globalThis.WorkerGlobalScope ){
+  const wPost = (type,payload)=>globalThis.postMessage({type, payload});
+  globalThis.onmessage = function({data}){
+    switch(data.type){
+      case 'run-tests':{
+        try{ runTests(); }
+        finally{ wPost('tests-end'); }
+        break;
+      }
+      default:
+        log("unhandled onmessage: ",data);
+        break;
+    }
+  };
+  sqt.setLogger((msg)=>{
+    wPost('stdout', {message: msg});
+  });
+  wPost('is-ready');
+  //globalThis.onmessage({data:{type:'run-tests'}});
+}else{
+  runTests();
 }
index 8ae3e27a36d1b351184f7a2908e2c1145d06c704..ebd828c645164e37e927d0e8cca4a13d16c1f05d 100644 (file)
     <link rel="stylesheet" href="../common/testing.css"/>
     <title>SQLTester</title>
   </head>
+  <style>
+    fieldset {
+        display: flex;
+        flex-direction: row;
+        padding-right: 1em;
+    }
+    fieldset > :not(.legend) {
+        display: flex;
+        flex-direction: row;
+        padding-right: 1em;
+    }
+  </style>
   <body>
-    <p>SQLTester App.
+    <h1>SQLTester for JS/WASM</h1>
+    <p>This app reads in a build-time-defined set of SQLTester test
+      scripts and runs them through the test suite.
     </p>
-    <p>All stuff on this page happens in the dev console.</p>
-    <hr>
+    <fieldset>
+      <legend>Options</legend>
+      <span class='input-wrapper'>
+        <input type='checkbox' id='cb-log-reverse' checked>
+        <label for='cb-log-reverse'>Reverse log order?</label>
+      </span>
+      <input type='button' id='btn-run-tests' value='Run tests'/>
+    </fieldset>
     <div id='test-output'></div>
     <!--script src='SQLTester.run.mjs' type='module'></script-->
     <script>
-      (function(){
-        // Noting that Firefox can't do this yet.
+      (async function(){
         const W = new Worker('SQLTester.run.mjs',{
           type: 'module'
         });
+        const wPost = (type,payload)=>W.postMessage({type,payload});
+        const mapToString = (v)=>{
+          switch(typeof v){
+            case 'string': return v;
+            case 'number': case 'boolean':
+            case 'undefined': case 'bigint':
+              return ''+v;
+            default: break;
+          }
+          if(null===v) return 'null';
+          if(v instanceof Error){
+            v = {
+              message: v.message,
+              stack: v.stack,
+              errorClass: v.name
+            };
+          }
+          return JSON.stringify(v,undefined,2);
+        };
+        const normalizeArgs = (args)=>args.map(mapToString);
+        const logTarget = document.querySelector('#test-output');
+        const logClass = function(cssClass,...args){
+          const ln = document.createElement('div');
+          if(cssClass){
+            for(const c of (Array.isArray(cssClass) ? cssClass : [cssClass])){
+              ln.classList.add(c);
+            }
+          }
+          ln.append(document.createTextNode(normalizeArgs(args).join(' ')));
+          logTarget.append(ln);
+        };
+        {
+          const cbReverse = document.querySelector('#cb-log-reverse');
+          const cbReverseKey = 'SQLTester:cb-log-reverse';
+          const cbReverseIt = ()=>{
+            logTarget.classList[cbReverse.checked ? 'add' : 'remove']('reverse');
+          };
+          cbReverse.addEventListener('change', cbReverseIt, true);
+          cbReverseIt();
+        }
+
+        const btnRun = document.querySelector('#btn-run-tests');
+        const runTests = ()=>{
+          btnRun.setAttribute('disabled','disabled');
+          wPost('run-tests');
+          logTarget.innerText = 'Running tests...';
+        }
+        btnRun.addEventListener('click', runTests);
+        const log2 = function f(...args){
+          logClass('', ...args);
+          return f;
+        };
+        const log = function f(...args){
+          logClass('','index.html:',...args);
+          return f;
+        };
+        W.onmessage = function({data}){
+          switch(data.type){
+            case 'stdout': log2(data.payload.message); break;
+            case 'tests-end': btnRun.removeAttribute('disabled'); break;
+            case 'is-ready':
+              log("SQLTester.run.mjs is ready.");
+              runTests(); break;
+            default:
+              log("unhandled onmessage",data);
+              break;
+          }
+        };
+        //runTests()
+        /* Inexplicably, */
       })();
     </script>
   </body>
index 7088c67f0588746e5bfd71a45e1fb8fb32a1ca7a..15a2c4be2dfd39d9224eb3c02ebe9b9ba00aea0d 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\sa\smechanism\swith\swhich\sto\simport\sexternal\sSQLTester\sscripts\sinto\sthe\sJS\stesting\stool.
-D 2023-08-30T00:22:54.642
+C Add\sa\sUI,\sof\ssorts,\sto\sthe\sJS\sSQLTester.
+D 2023-08-30T11:54:43.323
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -548,10 +548,10 @@ F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 27450c8b8c70875a260aca55435ec927068b34ce
 F ext/wasm/GNUmakefile 0e362f3fc04eab6628cbe4f1e35f4ab4a200881f6b5f753b27fb45eabeddd9d2
 F ext/wasm/README-dist.txt 6382cb9548076fca472fb3330bbdba3a55c1ea0b180ff9253f084f07ff383576
 F ext/wasm/README.md a8a2962c3aebdf8d2104a9102e336c5554e78fc6072746e5daf9c61514e7d193
-F ext/wasm/SQLTester/GNUmakefile ba0430646d75a832d1647d6d204c999112d831f5e85d3ed99d8f663fea20fe19
-F ext/wasm/SQLTester/SQLTester.mjs 378868be0fcbbb92456aea10e3056f88b042e106b2c348aa51060e93726f6e10
-F ext/wasm/SQLTester/SQLTester.run.mjs 2695490e2092af0c6a9d1e4128edf830648687a54ec1f0ecd16bd1be083f3938
-F ext/wasm/SQLTester/index.html 317636557257608b103fa740c07f2d440d57b924ef2072e59c1372d4a4004c06
+F ext/wasm/SQLTester/GNUmakefile e0794f676d55819951bbfae45cc5e8d7818dc460492dc317ce7f0d2eca15caff
+F ext/wasm/SQLTester/SQLTester.mjs 4a8fc2194a67180772830f503129c93fe44887b9573f50a4e24868b4bbc817f4
+F ext/wasm/SQLTester/SQLTester.run.mjs a50a1f9314d22d68b62a2f21d8913de163fce1fa420229711d6749c2b4fff9d0
+F ext/wasm/SQLTester/index.html e5f18af71749d81d86e4649d8c6efc9b78361738cb8e5c5014ba0dd3dc3de6ac
 F ext/wasm/SQLTester/touint8array.c 2d5ece04ec1393a6a60c4bf96385bda5e1a10ad49f3038b96460fc5e5aa7e536
 F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api d6a5078f48a5301ed17b9a30331075d9b2506e1360c1f0dee0c7816c10acd9ab
 F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-see fb29e62082a658f0d81102488414d422c393c4b20cc2f685b216bc566237957b
@@ -2113,8 +2113,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 7cef4a8300826adbdcb3b205e134a4272b12b4aa7dbee97731ac12282a4a9f06
-R 018b862c43a96a36d2df3223b087af85
+P bb08ba020ce1d86ca6aa92f43d5ae915f67d08fa73120e1f603d150e76166624
+R 8240e3c6439a4fca46c74a72b36ea777
 U stephan
-Z 18f8112180ee272e1f7e50a87a0ce726
+Z cef4bceac8cb673301c00562148f0e5f
 # Remove this line to create a well-formed Fossil manifest.
index 7168a7b8adc518b0851ae5ff9dfbdae1cc33832c..a0ecd6b9de2fded37c359de757db3d7c54e8ad8b 100644 (file)
@@ -1 +1 @@
-bb08ba020ce1d86ca6aa92f43d5ae915f67d08fa73120e1f603d150e76166624
\ No newline at end of file
+249e82b9917ea47c56ee1cbd3345a977d335fd3fc0d67a1ef157813ef4571c7c
\ No newline at end of file