Merge branch 'master' into peter
This commit is contained in:
commit
13c16acdd8
6 changed files with 199 additions and 85 deletions
|
|
@ -50,6 +50,85 @@ version on start. newfol does not accept schema files that have a newer version
|
||||||
than it currently supports, but it ignores directives it does not understand,
|
than it currently supports, but it ignores directives it does not understand,
|
||||||
and so can be manually downgraded by editing the `fmt` directive.
|
and so can be manually downgraded by editing the `fmt` directive.
|
||||||
|
|
||||||
|
Any record beginning with a `#` is ignored as a comment and preserved on
|
||||||
|
upgrades. Extra fields in a record are ignored.
|
||||||
|
|
||||||
|
The standard rules for formatting colon-separated values apply. In other words,
|
||||||
|
the format is defined as identical to the `text/csv` format specified in RFC
|
||||||
|
4180, except that the delimiter is a colon instead of a comma and that the line
|
||||||
|
break is a single linefeed instead of a CRLF pair.
|
||||||
|
|
||||||
|
==== Format Directives (`fmt`)
|
||||||
|
|
||||||
|
Format directives define the file as a schema file or configuration file as
|
||||||
|
specified in <<_schema_file>> and <<_configuration_file>>. They must appear
|
||||||
|
only at the very beginning of a schema or configuration file. In particular, it
|
||||||
|
is not allowed for them to be preceded by a byte order mark.
|
||||||
|
|
||||||
|
==== Table Definition Directives (`def`)
|
||||||
|
|
||||||
|
A table definition directive consists of a record containing the values `def`
|
||||||
|
and the table name. A table must be defined before any field definitions can
|
||||||
|
occur for that table.
|
||||||
|
|
||||||
|
==== Field Definition Directives (`fld` and `dsc`)
|
||||||
|
|
||||||
|
Field definitions come in two types. The first, `fld`, provides a mapping for
|
||||||
|
imported data, and `dsc` does not. They are otherwise identical.
|
||||||
|
|
||||||
|
A field definition directive consists of a record containing the values `fld` or
|
||||||
|
`dsc`, the name of the table, the field number for imported data (or empty for
|
||||||
|
`dsc`), the field number for use in newfol, an optional Python 3 expression, and
|
||||||
|
a description.
|
||||||
|
|
||||||
|
The Python 3 expression is evaluated and placed in the field when creating a new
|
||||||
|
record. This can be used, for example, to automatically insert the date into a
|
||||||
|
particular field. This requires that code execution has been allowed by an
|
||||||
|
`exe` directive.
|
||||||
|
|
||||||
|
==== Execution Directives (`exe` and `execute`)
|
||||||
|
|
||||||
|
Execution directives specify whether Python code will be run from the schema
|
||||||
|
file. Such a directive consists of a record containing either `exe` or
|
||||||
|
`execute` and then either `yes` or `no`.
|
||||||
|
|
||||||
|
Unlike other directives, where the schema file overrides the configuration file,
|
||||||
|
if any execution directive is set to `no`, execution is disabled. This is a
|
||||||
|
security feature to allow reading an untrusted database. Execution is also
|
||||||
|
disabled if more than one execution directive is seen in either the schema or
|
||||||
|
configuration file.
|
||||||
|
|
||||||
|
==== Key Field Directives (`key`)
|
||||||
|
|
||||||
|
Key field directives indicate the fields to display in the _Browse All Records_
|
||||||
|
view. By default, this value is 0 (display the first field).
|
||||||
|
|
||||||
|
==== Column Directives (`col`)
|
||||||
|
|
||||||
|
Column directives indicate how many columns to use to display fields. A column
|
||||||
|
directive consists of a record with the values `col` and an integer number of
|
||||||
|
columns.
|
||||||
|
|
||||||
|
==== Hook Directives (`txn` and `hook`)
|
||||||
|
|
||||||
|
Hook directives indicate a series of hooks to be run on each load or save
|
||||||
|
operation. Several different hooks are available. The default set of hooks is
|
||||||
|
`hash`. This provides an SHA-2 hash of the data in the `dtb.checksum` file, and
|
||||||
|
cannot be disabled, only replaced with a specific algorithm.
|
||||||
|
|
||||||
|
By default, the hash is `sha256` on 32-bit systems and `sha512` on 64-bit
|
||||||
|
systems; `sha384` is also available. If a specific algorithm is specified, it
|
||||||
|
overrides the `hash` default. Older versions of newfol only supported the
|
||||||
|
`sha256` algorithm.
|
||||||
|
|
||||||
|
The other available hook is `git`. This hook performs a commit every time the
|
||||||
|
file is saved, and a git checkout of the `dtb` file before every load. This
|
||||||
|
allows a user to keep a history of the data and to easily back it up to another
|
||||||
|
location.
|
||||||
|
|
||||||
|
A hook directive consists of a record starting with the value `txn`, and
|
||||||
|
followed by a list of hook names. `hook` may be used as a synonym for `txn`.
|
||||||
|
|
||||||
=== Configuration File
|
=== Configuration File
|
||||||
|
|
||||||
Configuration files are identical to schema files except that instead of the
|
Configuration files are identical to schema files except that instead of the
|
||||||
|
|
|
||||||
|
|
@ -169,7 +169,7 @@ class Schema:
|
||||||
self._nfields = nfields
|
self._nfields = nfields
|
||||||
self._mapping = {}
|
self._mapping = {}
|
||||||
self._keyfields = []
|
self._keyfields = []
|
||||||
self._exe_ok = False
|
self._exe_ok = None
|
||||||
self._imports = []
|
self._imports = []
|
||||||
self._layout = list(range(nfields))
|
self._layout = list(range(nfields))
|
||||||
self._cheatsheet = None
|
self._cheatsheet = None
|
||||||
|
|
@ -185,7 +185,7 @@ class Schema:
|
||||||
return self._txntype
|
return self._txntype
|
||||||
|
|
||||||
def execution_allowed(self):
|
def execution_allowed(self):
|
||||||
return self._exe_ok
|
return self._exe_ok or False
|
||||||
|
|
||||||
def mapping(self):
|
def mapping(self):
|
||||||
return self._mapping
|
return self._mapping
|
||||||
|
|
@ -329,7 +329,7 @@ class Schema:
|
||||||
mapping = {}
|
mapping = {}
|
||||||
keyfields = []
|
keyfields = []
|
||||||
nfields = 0
|
nfields = 0
|
||||||
exe_ok = False
|
exe_ok = None
|
||||||
exe_seen = False
|
exe_seen = False
|
||||||
for i in recs:
|
for i in recs:
|
||||||
if len(i.fields) == 0:
|
if len(i.fields) == 0:
|
||||||
|
|
@ -370,6 +370,8 @@ class Schema:
|
||||||
exe_seen = True
|
exe_seen = True
|
||||||
if i.fields[1].lower() in ("yes", "oui", "sí"):
|
if i.fields[1].lower() in ("yes", "oui", "sí"):
|
||||||
exe_ok = True
|
exe_ok = True
|
||||||
|
else:
|
||||||
|
exe_ok = False
|
||||||
elif rectype in ['imp', 'import']:
|
elif rectype in ['imp', 'import']:
|
||||||
self._imports = filter(lambda x: x, i.fields[1:])
|
self._imports = filter(lambda x: x, i.fields[1:])
|
||||||
elif rectype in ['pvu', 'preview']:
|
elif rectype in ['pvu', 'preview']:
|
||||||
|
|
@ -381,8 +383,14 @@ class Schema:
|
||||||
else:
|
else:
|
||||||
self._cheatsheet = path + "/" + cheatsheet
|
self._cheatsheet = path + "/" + cheatsheet
|
||||||
elif rectype in ['col', 'column']:
|
elif rectype in ['col', 'column']:
|
||||||
self._columns = int(i.fields[1])
|
try:
|
||||||
elif rectype in ['txn', 'transaction']:
|
self._columns = int(i.fields[1])
|
||||||
|
if self._columns <= 0:
|
||||||
|
raise ValueError
|
||||||
|
except ValueError:
|
||||||
|
raise newfol.exception.SchemaError("has invalid " +
|
||||||
|
"number of columns")
|
||||||
|
elif rectype in ['txn', 'transaction', 'hook']:
|
||||||
self._txntype = i.fields[1:]
|
self._txntype = i.fields[1:]
|
||||||
elif rectype in ['dpy', 'display']:
|
elif rectype in ['dpy', 'display']:
|
||||||
self._layout = list(map(fix_up_layout, i.fields[1:]))
|
self._layout = list(map(fix_up_layout, i.fields[1:]))
|
||||||
|
|
@ -400,7 +408,10 @@ class Schema:
|
||||||
self._keys[i.fields[1]] = None
|
self._keys[i.fields[1]] = None
|
||||||
self._mapping = mapping
|
self._mapping = mapping
|
||||||
self._keyfields = keyfields
|
self._keyfields = keyfields
|
||||||
self._exe_ok = exe_ok
|
if self._exe_ok is None:
|
||||||
|
self._exe_ok = exe_ok
|
||||||
|
elif exe_ok is False:
|
||||||
|
self._exe_ok = False
|
||||||
|
|
||||||
|
|
||||||
class Database:
|
class Database:
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ class LocaleError(NewfolError):
|
||||||
|
|
||||||
|
|
||||||
class SchemaError(NewfolError):
|
class SchemaError(NewfolError):
|
||||||
def __init__(msg):
|
def __init__(self, msg):
|
||||||
super().__init__("Schema file %s" % msg)
|
super().__init__("Schema file %s" % msg)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -28,7 +28,7 @@ class FilemanipError(Exception):
|
||||||
|
|
||||||
|
|
||||||
class CorruptFileError(FilemanipError):
|
class CorruptFileError(FilemanipError):
|
||||||
def __init__(type, msg):
|
def __init__(self, type, msg):
|
||||||
super().__init__("%s file is corrupt (%s)" % (type, msg))
|
super().__init__("%s file is corrupt (%s)" % (type, msg))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ class Record:
|
||||||
return self._version
|
return self._version
|
||||||
|
|
||||||
|
|
||||||
class TransactionStore:
|
class Hook:
|
||||||
def prepare_open(self, filename, mode):
|
def prepare_open(self, filename, mode):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
@ -87,7 +87,7 @@ class TransactionStore:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class StackingTransactionStore(TransactionStore):
|
class StackingHook(Hook):
|
||||||
def __init__(self, stores):
|
def __init__(self, stores):
|
||||||
self._stores = stores
|
self._stores = stores
|
||||||
|
|
||||||
|
|
@ -124,7 +124,7 @@ class StackingTransactionStore(TransactionStore):
|
||||||
store.commit_store(rec)
|
store.commit_store(rec)
|
||||||
|
|
||||||
|
|
||||||
class GitTransactionStore(TransactionStore):
|
class GitHook(Hook):
|
||||||
|
|
||||||
class DirectoryChanger:
|
class DirectoryChanger:
|
||||||
|
|
||||||
|
|
@ -198,7 +198,7 @@ class GitTransactionStore(TransactionStore):
|
||||||
self._call_git("commit", "-m", message)
|
self._call_git("commit", "-m", message)
|
||||||
|
|
||||||
|
|
||||||
class HashTransactionStore(TransactionStore):
|
class HashHook(Hook):
|
||||||
def __init__(self, hashname="sha256", options=None):
|
def __init__(self, hashname="sha256", options=None):
|
||||||
self._filename = None
|
self._filename = None
|
||||||
self._mode = None
|
self._mode = None
|
||||||
|
|
@ -230,7 +230,7 @@ class HashTransactionStore(TransactionStore):
|
||||||
return choice1
|
return choice1
|
||||||
if choice2 is not None:
|
if choice2 is not None:
|
||||||
return choice2
|
return choice2
|
||||||
return "sha512" if HashTransactionStore._bitness() == 64 else "sha256"
|
return "sha512" if HashHook._bitness() == 64 else "sha256"
|
||||||
|
|
||||||
def prepare_open(self, filename, mode):
|
def prepare_open(self, filename, mode):
|
||||||
self._filename = filename
|
self._filename = filename
|
||||||
|
|
@ -578,11 +578,11 @@ class FileStorage:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_transaction_types():
|
def _get_transaction_types():
|
||||||
return {
|
return {
|
||||||
"git": GitTransactionStore,
|
"git": GitHook,
|
||||||
"sha256": lambda *a, **k: HashTransactionStore("sha256", *a, **k),
|
"sha256": lambda *a, **k: HashHook("sha256", *a, **k),
|
||||||
"sha384": lambda *a, **k: HashTransactionStore("sha384", *a, **k),
|
"sha384": lambda *a, **k: HashHook("sha384", *a, **k),
|
||||||
"sha512": lambda *a, **k: HashTransactionStore("sha512", *a, **k),
|
"sha512": lambda *a, **k: HashHook("sha512", *a, **k),
|
||||||
"hash": lambda *a, **k: HashTransactionStore(None, *a, **k),
|
"hash": lambda *a, **k: HashHook(None, *a, **k),
|
||||||
}
|
}
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
@ -593,13 +593,13 @@ class FileStorage:
|
||||||
def _make_transaction_store(items, options):
|
def _make_transaction_store(items, options):
|
||||||
txntypes = FileStorage._get_transaction_types()
|
txntypes = FileStorage._get_transaction_types()
|
||||||
if items is None or items == "":
|
if items is None or items == "":
|
||||||
return TransactionStore()
|
return Hook()
|
||||||
elif isinstance(items, str):
|
elif isinstance(items, str):
|
||||||
return txntypes[items](options)
|
return txntypes[items](options)
|
||||||
else:
|
else:
|
||||||
stores = [FileStorage._make_transaction_store(i, options)
|
stores = [FileStorage._make_transaction_store(i, options)
|
||||||
for i in items]
|
for i in items]
|
||||||
return StackingTransactionStore(stores)
|
return StackingHook(stores)
|
||||||
|
|
||||||
def store(self, records):
|
def store(self, records):
|
||||||
"""Store the records."""
|
"""Store the records."""
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
from newfol.exception import SchemaError
|
||||||
from newfol.database import DatabaseVersion, Database, Schema, Singleton
|
from newfol.database import DatabaseVersion, Database, Schema, Singleton
|
||||||
from newfol.filemanip import Record
|
from newfol.filemanip import Record
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
@ -59,60 +60,56 @@ class TestDatabaseAccessors(unittest.TestCase):
|
||||||
DatabaseVersion.preferred().serialization())
|
DatabaseVersion.preferred().serialization())
|
||||||
|
|
||||||
|
|
||||||
class TestDatabaseIntegrity(unittest.TestCase):
|
class TemporaryDatabase:
|
||||||
def create_temp_db(self):
|
def __init__(self, schema_contents=""):
|
||||||
ddir = tempfile.TemporaryDirectory()
|
self.ddir = tempfile.TemporaryDirectory()
|
||||||
with open(ddir.name + "/schema", "w") as fp:
|
with open(self.ddir.name + "/schema", "w") as fp:
|
||||||
fp.write("fmt:0:newfol schema file:\ntxn:git\n")
|
fp.write("fmt:3:newfol schema file:\n" + schema_contents)
|
||||||
db = Database.load(ddir.name)
|
|
||||||
return (ddir, db)
|
|
||||||
|
|
||||||
|
@property
|
||||||
|
def db(self):
|
||||||
|
return Database.load(self.ddir.name)
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
self.ddir.cleanup()
|
||||||
|
|
||||||
|
|
||||||
|
class TestDatabaseIntegrity(unittest.TestCase):
|
||||||
def test_version(self):
|
def test_version(self):
|
||||||
ddir, db = self.create_temp_db()
|
tdb = TemporaryDatabase()
|
||||||
self.assertEqual(Database.read_version(ddir.name),
|
self.assertEqual(Database.read_version(tdb.ddir.name),
|
||||||
DatabaseVersion())
|
DatabaseVersion())
|
||||||
db.store()
|
tdb.db.store()
|
||||||
db.upgrade()
|
tdb.db.upgrade()
|
||||||
self.assertEqual(Database.read_version(ddir.name),
|
self.assertEqual(Database.read_version(tdb.ddir.name),
|
||||||
DatabaseVersion.preferred())
|
DatabaseVersion.preferred())
|
||||||
ddir.cleanup()
|
|
||||||
|
|
||||||
def test_validate(self):
|
def test_validate(self):
|
||||||
ddir, db = self.create_temp_db()
|
tdb = TemporaryDatabase()
|
||||||
db.store()
|
tdb.db.store()
|
||||||
db.validate()
|
tdb.db.validate()
|
||||||
ddir.cleanup()
|
|
||||||
|
|
||||||
def test_validate_strict(self):
|
def test_validate_strict(self):
|
||||||
ddir, db = self.create_temp_db()
|
tdb = TemporaryDatabase()
|
||||||
db.store()
|
tdb.db.store()
|
||||||
db.validate(strict=True)
|
tdb.db.validate(strict=True)
|
||||||
ddir.cleanup()
|
|
||||||
|
|
||||||
def test_repair_doesnt_raise(self):
|
def test_repair_doesnt_raise(self):
|
||||||
ddir, db = self.create_temp_db()
|
tdb = TemporaryDatabase()
|
||||||
db.store()
|
tdb.db.store()
|
||||||
db.repair()
|
tdb.db.repair()
|
||||||
db.validate(strict=True)
|
tdb.db.validate(strict=True)
|
||||||
ddir.cleanup()
|
|
||||||
|
|
||||||
def test_upgrade_records(self):
|
def test_upgrade_records(self):
|
||||||
ddir, db = self.create_temp_db()
|
tdb = TemporaryDatabase()
|
||||||
db.store()
|
tdb.db.store()
|
||||||
db.upgrade_records()
|
tdb.db.upgrade_records()
|
||||||
ddir.cleanup()
|
|
||||||
|
|
||||||
|
|
||||||
class TestDatabaseUpgrades(unittest.TestCase):
|
class TestDatabaseUpgrades(unittest.TestCase):
|
||||||
def create_temp_db(self):
|
|
||||||
ddir = tempfile.TemporaryDirectory()
|
|
||||||
with open(ddir.name + "/schema", "w") as fp:
|
|
||||||
fp.write("fmt:0:newfol schema file:\ntxn:git\n")
|
|
||||||
db = Database.load(ddir.name)
|
|
||||||
return (ddir, db)
|
|
||||||
|
|
||||||
def do_upgrade_test(self, version, pattern):
|
def do_upgrade_test(self, version, pattern):
|
||||||
ddir, db = self.create_temp_db()
|
tdb = TemporaryDatabase("txn:git\n")
|
||||||
|
ddir, db = tdb.ddir, tdb.db
|
||||||
if not isinstance(version, DatabaseVersion):
|
if not isinstance(version, DatabaseVersion):
|
||||||
version = DatabaseVersion(version)
|
version = DatabaseVersion(version)
|
||||||
self.assertEqual(Database.read_version(ddir.name),
|
self.assertEqual(Database.read_version(ddir.name),
|
||||||
|
|
@ -141,56 +138,84 @@ class TestDatabaseUpgrades(unittest.TestCase):
|
||||||
|
|
||||||
class TestMultipleTransactions(unittest.TestCase):
|
class TestMultipleTransactions(unittest.TestCase):
|
||||||
def test_multiple_types(self):
|
def test_multiple_types(self):
|
||||||
ddir = tempfile.TemporaryDirectory()
|
tdb = TemporaryDatabase("txn:git:hash\n")
|
||||||
with open(ddir.name + "/schema", "w") as fp:
|
ddir = tdb.ddir
|
||||||
fp.write("fmt:0:newfol schema file:\ntxn:git:hash\n")
|
|
||||||
db = Database.load(ddir.name)
|
db = Database.load(ddir.name)
|
||||||
db.records()[:] = [Record([1, 2, 3])]
|
db.records()[:] = [Record([1, 2, 3])]
|
||||||
db.store()
|
db.store()
|
||||||
self.assertEqual(set(db.schema().transaction_types()),
|
self.assertEqual(set(db.schema().transaction_types()),
|
||||||
set(["git", "hash"]))
|
set(["git", "hash"]))
|
||||||
ddir.cleanup()
|
|
||||||
|
|
||||||
|
|
||||||
class TestExtraSchemaConfig(unittest.TestCase):
|
class TestExtraSchemaConfig(unittest.TestCase):
|
||||||
def test_existing_config_file(self):
|
def test_existing_config_file(self):
|
||||||
ddir1 = tempfile.TemporaryDirectory()
|
tdb = TemporaryDatabase()
|
||||||
ddir2 = tempfile.TemporaryDirectory()
|
ddir2 = tempfile.TemporaryDirectory()
|
||||||
config = "%s/config" % ddir2.name
|
config = "%s/config" % ddir2.name
|
||||||
with open(ddir1.name + "/schema", "w") as fp:
|
|
||||||
fp.write("fmt:3:newfol schema file:\n")
|
|
||||||
with open(config, "w") as fp:
|
with open(config, "w") as fp:
|
||||||
fp.write("fmt:3:newfol config file:\ntxn:git:hash\n")
|
fp.write("fmt:3:newfol config file:\ntxn:git:hash\n")
|
||||||
db = Database.load(ddir1.name, extra_config=[config])
|
db = Database.load(tdb.ddir.name, extra_config=[config])
|
||||||
db.records()[:] = [Record([1, 2, 3])]
|
db.records()[:] = [Record([1, 2, 3])]
|
||||||
db.store()
|
db.store()
|
||||||
self.assertEqual(set(db.schema().transaction_types()),
|
self.assertEqual(set(db.schema().transaction_types()),
|
||||||
set(["git", "hash"]))
|
set(["git", "hash"]))
|
||||||
ddir1.cleanup()
|
|
||||||
ddir2.cleanup()
|
ddir2.cleanup()
|
||||||
|
|
||||||
def test_missing_config_file(self):
|
def test_missing_config_file(self):
|
||||||
ddir1 = tempfile.TemporaryDirectory()
|
tdb = TemporaryDatabase()
|
||||||
config = "%s/config" % ddir1.name
|
config = "%s/config" % tdb.ddir.name
|
||||||
with open(ddir1.name + "/schema", "w") as fp:
|
db = Database.load(tdb.ddir.name, extra_config=[config])
|
||||||
fp.write("fmt:3:newfol schema file:\n")
|
|
||||||
db = Database.load(ddir1.name, extra_config=[config])
|
|
||||||
db.records()[:] = [Record([1, 2, 3])]
|
db.records()[:] = [Record([1, 2, 3])]
|
||||||
db.store()
|
db.store()
|
||||||
# Ensure no exception is raised.
|
# Ensure no exception is raised.
|
||||||
ddir1.cleanup()
|
|
||||||
|
|
||||||
|
class TestSchemaColumns(unittest.TestCase):
|
||||||
|
def check_invalid(self, value):
|
||||||
|
with self.assertRaises(SchemaError):
|
||||||
|
tdb = TemporaryDatabase("col:%s\n" % value)
|
||||||
|
tdb.db
|
||||||
|
|
||||||
|
def test_non_integral(self):
|
||||||
|
self.check_invalid(3.5)
|
||||||
|
|
||||||
|
def test_negative(self):
|
||||||
|
self.check_invalid(-1)
|
||||||
|
|
||||||
|
def test_zero(self):
|
||||||
|
self.check_invalid(0)
|
||||||
|
|
||||||
|
|
||||||
|
class TestExecutionAllowed(unittest.TestCase):
|
||||||
|
def do_test(self, expected, schema, configv):
|
||||||
|
tdb = TemporaryDatabase(schema)
|
||||||
|
ddir2 = tempfile.TemporaryDirectory()
|
||||||
|
config = "%s/config" % ddir2.name
|
||||||
|
with open(config, "w") as fp:
|
||||||
|
fp.write("fmt:3:newfol config file:\n" + configv)
|
||||||
|
db = Database.load(tdb.ddir.name, extra_config=[config])
|
||||||
|
db.records()[:] = [Record([1, 2, 3])]
|
||||||
|
db.store()
|
||||||
|
self.assertEqual(db.schema().execution_allowed(), expected)
|
||||||
|
ddir2.cleanup()
|
||||||
|
|
||||||
|
def test_true_if_only_true_schema(self):
|
||||||
|
self.do_test(True, "exe:yes\n", "")
|
||||||
|
|
||||||
|
def test_true_if_only_true_config(self):
|
||||||
|
self.do_test(True, "", "exe:yes\n")
|
||||||
|
|
||||||
|
def test_false_if_schema_false(self):
|
||||||
|
self.do_test(False, "exe:no\n", "exe:yes\n")
|
||||||
|
|
||||||
|
def test_false_if_config_false(self):
|
||||||
|
self.do_test(False, "exe:yes\n", "exe:no\n")
|
||||||
|
|
||||||
|
|
||||||
class TestDatabaseFiltering(unittest.TestCase):
|
class TestDatabaseFiltering(unittest.TestCase):
|
||||||
def create_temp_db(self):
|
|
||||||
ddir = tempfile.TemporaryDirectory()
|
|
||||||
with open(ddir.name + "/schema", "w") as fp:
|
|
||||||
fp.write("fmt:0:newfol schema file:\ntxn:git\n")
|
|
||||||
db = Database.load(ddir.name)
|
|
||||||
return (ddir, db)
|
|
||||||
|
|
||||||
def test_filtering(self):
|
def test_filtering(self):
|
||||||
ddir, db = self.create_temp_db()
|
tdb = TemporaryDatabase("txn:git\n")
|
||||||
|
db = tdb.db
|
||||||
records = [
|
records = [
|
||||||
Record(["a", "b", "c"]),
|
Record(["a", "b", "c"]),
|
||||||
Record([1, 2, 3]),
|
Record([1, 2, 3]),
|
||||||
|
|
@ -208,7 +233,6 @@ class TestDatabaseFiltering(unittest.TestCase):
|
||||||
selected = db.records(has_only_numbers)
|
selected = db.records(has_only_numbers)
|
||||||
self.assertEqual(type(selected), list)
|
self.assertEqual(type(selected), list)
|
||||||
self.assertEqual(set(selected), set(records[1:]))
|
self.assertEqual(set(selected), set(records[1:]))
|
||||||
ddir.cleanup()
|
|
||||||
|
|
||||||
|
|
||||||
class TestSingleton(unittest.TestCase):
|
class TestSingleton(unittest.TestCase):
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
|
|
||||||
from newfol.filemanip import Record, FileStorage, HashTransactionStore
|
from newfol.filemanip import Record, FileStorage, HashHook
|
||||||
import hashlib
|
import hashlib
|
||||||
import newfol.exception
|
import newfol.exception
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
@ -140,7 +140,7 @@ class SHA256TransactionTest(unittest.TestCase):
|
||||||
v = self.generate_len_and_hash(hashlib.sha256, k)
|
v = self.generate_len_and_hash(hashlib.sha256, k)
|
||||||
with open(temp, "w") as wfp:
|
with open(temp, "w") as wfp:
|
||||||
wfp.write(k)
|
wfp.write(k)
|
||||||
self.assertEqual(HashTransactionStore._hash_file("sha256", temp),
|
self.assertEqual(HashHook._hash_file("sha256", temp),
|
||||||
"sha256:" + v)
|
"sha256:" + v)
|
||||||
tempdir.cleanup()
|
tempdir.cleanup()
|
||||||
|
|
||||||
|
|
@ -210,7 +210,7 @@ class SHA256TransactionTest(unittest.TestCase):
|
||||||
x.fs.store([Record(["", ""])])
|
x.fs.store([Record(["", ""])])
|
||||||
with open(x.tempfile + ".checksum", "r") as fp:
|
with open(x.tempfile + ".checksum", "r") as fp:
|
||||||
data = fp.read()
|
data = fp.read()
|
||||||
bitness = HashTransactionStore._bitness()
|
bitness = HashHook._bitness()
|
||||||
self.assertIn(bitness, [32, 64])
|
self.assertIn(bitness, [32, 64])
|
||||||
self.assertTrue(data.startswith("sha512" if bitness == 64 else
|
self.assertTrue(data.startswith("sha512" if bitness == 64 else
|
||||||
"sha256"))
|
"sha256"))
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue