[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [sbws/master] Add State class and 100% test coverage for it
commit d3e1025bacd796eabe5285206762a858bf6d867a
Author: Matt Traudt <sirmatt@xxxxxxx>
Date: Fri Jul 13 12:07:03 2018 -0400
Add State class and 100% test coverage for it
GH: ref #166
---
sbws/util/state.py | 62 ++++++++++++++++++++++
tests/unit/util/test_state.py | 120 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 182 insertions(+)
diff --git a/sbws/util/state.py b/sbws/util/state.py
new file mode 100644
index 0000000..58aef38
--- /dev/null
+++ b/sbws/util/state.py
@@ -0,0 +1,62 @@
+from sbws.util.filelock import FileLock
+import os
+import json
+
+
+class State:
+ _ALLOWED_TYPES = (int, float, str, bool, type(None))
+
+ def __init__(self, fname):
+ self._fname = fname
+ self._state = self._read()
+
+ def _read(self):
+ with FileLock(self._fname):
+ if not os.path.exists(self._fname):
+ return {}
+ with open(self._fname, 'rt') as fd:
+ return json.load(fd)
+
+ def _write(self):
+ with FileLock(self._fname):
+ with open(self._fname, 'wt') as fd:
+ return json.dump(self._state, fd)
+
+ def __len__(self):
+ self._state = self._read()
+ return self._state.__len__()
+
+ def __getitem__(self, key):
+ if not isinstance(key, str):
+ raise TypeError(
+ 'Keys must be strings. %s is a %s' % (key, type(key)))
+ self._state = self._read()
+ return self._state.__getitem__(key)
+
+ def __delitem__(self, key):
+ if not isinstance(key, str):
+ raise TypeError(
+ 'Keys must be strings. %s is a %s' % (key, type(key)))
+ self._state = self._read()
+ self._state.__delitem__(key)
+ self._write()
+
+ def __setitem__(self, key, value):
+ if not isinstance(key, str):
+ raise TypeError(
+ 'Keys must be strings. %s is a %s' % (key, type(key)))
+ if type(value) not in State._ALLOWED_TYPES:
+ raise TypeError(
+ 'May only store value with type in %s, not %s' %
+ (State._ALLOWED_TYPES, type(value)))
+ self._state = self._read()
+ self._state.__setitem__(key, value)
+ self._write()
+
+ def __iter__(self):
+ self._state = self._read()
+ return self._state.__iter__()
+
+ def __contains__(self, item):
+ self._state = self._read()
+ return self._state.__contains__(item)
diff --git a/tests/unit/util/test_state.py b/tests/unit/util/test_state.py
new file mode 100644
index 0000000..42a6a8e
--- /dev/null
+++ b/tests/unit/util/test_state.py
@@ -0,0 +1,120 @@
+from sbws.util.state import State
+import os
+# from tempfile import NamedTemporaryFile as NTF
+
+
+def test_state_set_allowed_key_types(tmpdir):
+ state = State(os.path.join(tmpdir, 'statefoo'))
+ attempt_keys = ('k')
+ for key in attempt_keys:
+ state[key] = 4
+ assert state[key] == 4
+
+
+def test_state_set_bad_key_types(tmpdir):
+ state = State(os.path.join(tmpdir, 'statefoo'))
+ attempt_keys = (15983, None, True, -1.2, [], {}, set())
+ for key in attempt_keys:
+ try:
+ state[key] = 4
+ except TypeError:
+ pass
+ else:
+ assert None, 'Should not have been able to use %s %s as a key' %\
+ (key, type(key))
+ try:
+ state[key]
+ except TypeError:
+ pass
+ else:
+ assert None, '%s %s is not a valid key type, so should have got '\
+ 'TypeError when giving it' % (key, type(key))
+
+
+def test_state_set_allowed_value_types(tmpdir):
+ state = State(os.path.join(tmpdir, 'statefoo'))
+ attempt_vals = (15983, None, True, -1.2, 'loooooool')
+ for val in attempt_vals:
+ state['foo'] = val
+ assert state['foo'] == val
+
+
+def test_state_set_bad_value_types(tmpdir):
+ state = State(os.path.join(tmpdir, 'statefoo'))
+ attempt_vals = ([], {}, set())
+ for val in attempt_vals:
+ try:
+ state['foo'] = val
+ except TypeError:
+ pass
+ else:
+ assert None, 'Should not have been able to use %s %s as a value' %\
+ (val, type(val))
+
+
+def test_state_del(tmpdir):
+ state = State(os.path.join(tmpdir, 'statefoo'))
+ d = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
+ for key in d:
+ state[key] = d[key]
+ assert len(state) == len(d)
+
+ del d['a']
+ del state['a']
+ assert len(state) == len(d)
+ for key in d:
+ assert d[key] == state[key]
+
+ attempt_keys = (15983, None, True, -1.2, [], {}, set())
+ for key in attempt_keys:
+ try:
+ del state[False]
+ except TypeError:
+ pass
+ else:
+ assert None, 'Should not have been allowed to delete %s %s '\
+ 'because it is not a valid key type' % (key, type(key))
+
+ d['e'] = 5
+ state['e'] = 5
+ d['e'] = 5.5
+ state['e'] = 5.5
+ assert len(state) == len(d)
+
+
+def test_state_get_len(tmpdir):
+ state = State(os.path.join(tmpdir, 'statefoo'))
+ d = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
+ for key in d:
+ state[key] = d[key]
+ assert len(state) == len(d)
+
+ del d['a']
+ del state['a']
+ assert len(state) == len(d)
+
+ d['e'] = 5
+ state['e'] = 5
+ d['e'] = 5.5
+ state['e'] = 5.5
+ assert len(state) == len(d)
+
+
+def test_state_contains(tmpdir):
+ state = State(os.path.join(tmpdir, 'statefoo'))
+ d = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
+ for key in d:
+ state[key] = d[key]
+ assert 'a' in state
+ assert 'e' not in state
+
+
+def test_state_iter(tmpdir):
+ state = State(os.path.join(tmpdir, 'statefoo'))
+ for key in state:
+ pass
+ d = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
+ for key in d:
+ state[key] = d[key]
+ for key in state:
+ pass
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits