/*
Unit tests and demo of the [versionedsettings] and [settingsmetadata]
vtables.
*/
if(1){
s2out << __FLC << ": versionedsettings and settingsmetadata tests disabled because "
<< "they are inexplicably flaky.\n";
return;
}
const f = Fossil.Context.new({
//traceSql: true // enable this for a more educational experience
}).openCheckout();
const vSettingsDir = f.db.selectValue("select fsl_ckout_dir()||'.fossil-settings/'");
affirm vSettingsDir.indexOf('.fossil-settings') > 0;
scope {
const globVal = f.db.selectValue("select value from versionedsettings where name='binary-glob'");
affirm globVal.indexOf('*.png')>=0;
var count = 0;
f.db.each({
sql: 'select /*name as name, value as value*/ * from versionedsettings',
callback: proc(){
++count;
if(1===rowNumber){
//print(__FLC,columnNames);
affirm 2 === columnNames.length();
/* TODO: check column names, once they're final */
}
}
});
affirm count > 2 /* libfossil has 3 as of this writing */;
}
/**
A helper to get a versioned setting. If no such setting is found,
then: if useMetaDefault is truthy and its fossil(1)-defined default
value is returned, otherwise undefined is returned.
*/
f.vsGet = proc(key,useMetaDefault=true){
affirm 'string' === typename key;
return this.db.selectValue((useMetaDefault ? fmtMeta : fmtSimple), key);
}.importSymbols({
fmtSimple: "select value from versionedsettings where name=?1",
fmtMeta: <<<SQL
select coalesce(v.value,m.default_value) from settingsmetadata m left join versionedsettings v
on v.name=m.name where m.name=?1 SQL
});
if(0){
print(__FLC,"Transaction tests disabled by stephan on 20141121 because of inexplicable weirdness at a time where other priorities take precedence.")
} || scope {
const FS = Fossil.file; // convenience shortcut
const db = f.db; // convenience shortcut
const vsetting = 'allow-symlinks' /* the name of a setting libf's core repo does not currently use */;
const vfile = vSettingsDir + vsetting /* path to vsettings' file */;
const defaultVal = db.selectValue(
// fossil-level default value for vsetting
'select default_value from settingsmetadata where name=%1$Q'.applyFormat(vsetting)
);
affirm undefined !== defaultVal /* Expecting 'on' or 'off' or similar. */;
// Make sure we'v got a clean working slate...
catch FS.unlink(vfile);
affirm !FS.isAccessible(vfile) /* Expecting to have no file for vsetting */;
affirm 0 === db.transactionState() /* This test requires that no higher-level transaction be active. */;
//print(__FLC,'stat(',vfile,') =',FS.stat(vfile));
//print(__FLC, 'select default_value from settingsmetadata where name=%1$Q'.applyFormat(vsetting));
//print(__FLC,defaultVal, f.vsGet(vsetting));
//s2out << __FLC<< ': defaultVal = '<<defaultVal<< ', f.vsGet(vsetting) = '<<f.vsGet(vsetting)<<'\n';
affirm defaultVal === f.vsGet(vsetting) /* sanity check */;
affirm undefined === f.vsGet(vsetting,false) /* sanity check */;
var ex = catch db.selectValue(
"insert into versionedsettings(name,value) values('unknown-key',null)"
);
affirm ex /* Expecting exception for unknown vsettings key. */;
//print(ex.codeString(),ex);
affirm ex.message.indexOf('must be')>0 /* expecting error message to contain this substring */;
affirm defaultVal === f.vsGet(vsetting) /* Expecting the insert to have failed. */;
unset ex;
// Make sure that transactions behave as we expect them to...
db.transaction(proc(){
this.selectValue(
"insert into versionedsettings(name,value) values('%1$q','yes')".applyFormat(vsetting)
);
affirm 'yes' === f.vsGet(vsetting) /* Expecting INSERT to have written this. */;
//print(__FLC,FS.stat(vfile));
affirm !FS.isAccessible(vfile) /* The new file is not written until commit. */;
});
affirm FS.isAccessible(vfile);
affirm 0 === db.transactionState() /* Expecting prev. transaction() to have been the top-most transaction. */;
affirm 'yes' === f.vsGet(vsetting);
/**
2021-02-08: this select fails with an sqlite error if run from inside transaction():
"cannot delete with open cursors". Whether that's due to a bug in the vtable or
a change in sqlite's behavior since it was implemented is unknown
*/
db.selectValue(
"delete from versionedsettings where name=?1",
vsetting
);
affirm !FS.isAccessible(vfile) /* file deleted, right? */;
affirm defaultVal === f.vsGet(vsetting) /* Expecting DELETE to revert this. */;
db.transaction(proc(){
this.selectValue(
"insert or replace into versionedsettings(name,value) values($name,$no)",
{$name:vsetting, $no: 'no'}
);
affirm !FS.isAccessible(vfile) /* New files not written until commit. */;
affirm 'no' === f.vsGet(vsetting) /* Expecting to be able to read back
the new value before COMMIT */;
});
affirm 0 === db.transactionState() /* Expecting prev. transaction() to have been the top-most transaction. */;
//print(__FLC,'stat(',vfile,') =',FS.stat(vfile));
affirm FS.isAccessible(vfile) /* Expecting transaction to write it. */;
affirm 'no' === f.vsGet(vsetting) /* Expecting transaction to write this. */;
db.transaction(proc(){
this.selectValue(
"delete from versionedsettings where name=?1",
[vsetting]
);
affirm FS.isAccessible(vfile) /* Not removed until commit. */;
affirm defaultVal === f.vsGet(vsetting) /* Expecting DELETE to revert this. */;
});
affirm 0 === db.transactionState() /* Expecting prev. transaction() to have been the top-most transaction. */;
affirm !FS.isAccessible(vfile) /* Expecting transaction to delete it. */;
affirm defaultVal === f.vsGet(vsetting) /* Expecting to be back to our starting point. */;
}