[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