forked from controversial/livejson
-
Notifications
You must be signed in to change notification settings - Fork 0
/
test.py
290 lines (254 loc) · 10.6 KB
/
test.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
import os
import unittest
import livejson
class _BaseTest():
path = "test_file.json"
def tearDown(self):
""" Called after each test to remove the file """
if os.path.exists(self.path):
os.remove(self.path)
class TestFile(_BaseTest, unittest.TestCase):
""" Test the magical JSON file class """
def test_DictFile(self):
""" Test that 'livejson.File's in which the base object is a dict work
as expected. This also tests all the methods shared between both types.
"""
# Test that a blank JSON file can be properly created
f = livejson.File(self.path)
self.assertIsInstance(f, livejson.DictFile) # Test DictFile is default
self.assertTrue(os.path.exists(self.path))
with open(self.path, "r") as fi:
self.assertEqual(fi.read(), "{}")
# Test writing to a file
f["a"] = "b"
# Test reading values from an existing file
newInstance = livejson.DictFile(self.path).data # Tests explicit type
self.assertEqual(newInstance["a"], "b")
# Test deleting values
f["c"] = "d"
self.assertIn("c", f) # This also conviently tests __contains__
del f["c"]
self.assertNotIn("c", f)
def test_ListFile(self):
""" Test that Files in which the base object is an array work """
# Create the JSON file.
f = livejson.ListFile(self.path)
self.assertEqual(f.data, [])
# Test append, extend, and insert
f.append("dogs")
f.extend(["cats", "penguins"])
f.insert(0, "turtles")
self.assertIsInstance(f.data, list)
self.assertEqual(f.data, ["turtles", "dogs", "cats", "penguins"])
# Test clear
f.clear()
self.assertEqual(len(f), 0)
# Test creating a new ListFile automatically when file is an Array
f2 = livejson.File(self.path)
self.assertIsInstance(f2, livejson.ListFile)
def test_special_stuff(self):
""" Test all the not-strictly-necessary extra API that I added """
f = livejson.File(self.path)
f["a"] = "b"
# Test 'data' (get a vanilla dict object)
self.assertEqual(f.data, {"a": "b"})
# Test file_contents
self.assertEqual(f.file_contents, "{\"a\": \"b\"}")
# Test __str__ and __repr__
self.assertEqual(str(f), str(f.data))
self.assertEqual(repr(f), repr(f.data))
# Test __iter__
self.assertEqual(list(f), list(f.keys()))
# Test remove()
f.remove()
self.assertFalse(os.path.exists(self.path))
def test_switchclass(self):
""" Test that it can automatically switch classes """
# Test switching under normal usage
f = livejson.File(self.path)
self.assertIsInstance(f, livejson.DictFile)
f.set_data([])
self.assertIsInstance(f, livejson.ListFile)
# Test switching when the file is manually changed
with open(self.path, "w") as fi:
fi.write("{}")
# This shouldn't error, it should change types when you do this
f["dogs"] = "cats"
self.assertIsInstance(f, livejson.DictFile)
def test_staticmethod_initalization(self):
""" Test initializing the File in special ways with custom
staticmethods """
f = livejson.File.with_data(self.path, ["a", "b", "c"])
self.assertEqual(f.data, ["a", "b", "c"])
# Test initialization from JSON string
os.remove(self.path)
f2 = livejson.File.with_data(self.path, "[\"a\", \"b\", \"c\"]")
self.assertEqual(len(f2), 3)
def test_errors(self):
""" Test the errors that are set up """
f = livejson.File(self.path)
# Test error for trying to initialize in non-existant directories
self.assertRaises(IOError, livejson.File, "a/b/c.py")
# Test error when trying to store non-string keys
with self.assertRaises(TypeError):
f[True] = "test"
# Test that storing numeric keys raises a more helpful error message
with self.assertRaisesRegexp(TypeError, "Try using a"):
f[0] = "abc"
# When initializing using with_data, test that an error is thrown if
# the file already exists
with self.assertRaises(ValueError):
livejson.File.with_data(self.path, {})
def test_empty_file(self):
""" Test that a File can be initialized in a completely empty, but
existing, file """
# Dict files
with open(self.path, "w") as fi:
fi.write("")
f = livejson.File(self.path)
self.assertEqual(f.data, {})
# List files
with open(self.path, "w") as fi:
fi.write("")
f = livejson.ListFile(self.path)
self.assertEqual(f.data, [])
def test_rollback(self):
""" Test that data can be restored in the case of an error to prevent
corruption (see #3)"""
class Test (object):
pass
f = livejson.File(self.path)
f["a"] = "b"
with self.assertRaises(TypeError):
f["test"] = Test()
self.assertEqual(f.data, {"a": "b"})
def test_json_formatting(self):
""" Test the extra JSON formatting options """
# Test pretty formatting
f = livejson.File(self.path, pretty=True)
f["a"] = "b"
self.assertEqual(f.file_contents, '{\n "a": "b"\n}')
f.indent = 4
f.set_data(f.data) # Force an update
self.assertEqual(f.file_contents, '{\n "a": "b"\n}')
# Test sorting of keys
f["b"] = "c"
f["d"] = "e"
f["c"] = "d"
self.assertTrue(f.file_contents.find("a") <
f.file_contents.find("b") <
f.file_contents.find("c") <
f.file_contents.find("d")
)
class TestNesting(_BaseTest, unittest.TestCase):
def test_list_nesting(self):
""" Test the nesting of lists inside a livejson.File """
f = livejson.File(self.path)
f["stored_data"] = {}
f["stored_data"]["test"] = "value"
self.assertEqual(f.data, {"stored_data": {"test": "value"}})
def test_dict_nesting(self):
""" Test the nesting of dicts inside a livejson.File """
f = livejson.File(self.path)
f["stored_data"] = []
f["stored_data"].append("test")
self.assertEqual(f.data, {"stored_data": ["test"]})
def test_multilevel_nesting(self):
""" Test that you can nest stuff inside nested stuff :O """
f = livejson.File(self.path)
f["stored_data"] = []
f["stored_data"].append({})
f["stored_data"][0]["colors"] = ["green", "purple"]
self.assertEqual(f.data,
{"stored_data": [{"colors": ["green", "purple"]}]}
)
def test_misc_methods(self):
f = livejson.File(self.path)
f["stored_data"] = [{"colors": ["green"]}]
# Test that normal __getitem__ still works
self.assertEqual(f["stored_data"][0]["colors"][0], "green")
# Test deleting values
f["stored_data"][0]["colors"].pop(0)
self.assertEqual(len(f["stored_data"][0]["colors"]), 0)
# Test __iter__ on nested dict
f["stored_data"] = {"a": "b", "c": "d"}
self.assertEqual(list(f["stored_data"]),
list(f["stored_data"].keys()))
def test_errors(self):
""" Test the errors that are thrown """
f = livejson.File(self.path)
f["data"] = {}
# Test that storing non-string keys in a nested dict throws an error
with self.assertRaises(TypeError):
f["data"][True] = "test"
# Test that storing numeric keys raises an additional error message
with self.assertRaisesRegexp(TypeError, "Try using a"):
f["data"][0] = "abc"
class TestGroupedWrites(_BaseTest, unittest.TestCase):
""" Test using "grouped writes" with the context manager. These improve
efficiency by only writing to the file once, at the end, instead of
writing every change as it is made. """
def test_basics(self):
f = livejson.File(self.path)
with f:
f["a"] = "b"
# Make sure that the write doesn't happen until we exit
self.assertEqual(f.file_contents, "{}")
self.assertEqual(f.file_contents, "{\"a\": \"b\"}")
def test_with_existing_file(self):
""" Test that the with block won't clear data """
f = livejson.File(self.path)
f["a"] = "b"
with f:
f["c"] = "d"
self.assertIn("a", f)
def test_lists(self):
f = livejson.ListFile(self.path)
with f:
for i in range(10):
f.append(i)
self.assertEqual(f.file_contents, "[]")
self.assertEqual(len(f), 10)
def test_switchclass(self):
""" Test the switching of classes in the middle of a grouped write """
f = livejson.File(self.path)
with f:
self.assertIsInstance(f, livejson.DictFile)
f.set_data([])
self.assertIsInstance(f, livejson.ListFile)
self.assertEqual(f.file_contents, "{}")
self.assertEqual(f.file_contents, "[]")
def test_misc(self):
""" Test miscellaneous other things that seem like they might break
with a grouped write """
f = livejson.File(self.path)
# Test is_caching, and test that data works with the cache
self.assertEqual(f.is_caching, False)
with f:
self.assertEqual(f.is_caching, True)
f["a"] = "b"
# Test that data reflects the cache
self.assertEqual(f.data, {"a": "b"})
self.assertEqual(f.is_caching, False)
def test_fun_syntax(self):
""" This is a fun bit of "syntactic sugar" enabled as a side effect of
grouped writes. """
with livejson.File(self.path) as f:
f["cats"] = "dogs"
with open(self.path, "r") as fi:
self.assertEqual(fi.read(), "{\"cats\": \"dogs\"}")
class TestAliases(_BaseTest, unittest.TestCase):
def test_Database(self):
db = livejson.Database(self.path)
self.assertTrue(os.path.exists(self.path))
self.assertEqual(db.data, {})
def test_ListDatabase(self):
db = livejson.ListDatabase(self.path)
self.assertTrue(os.path.exists(self.path))
self.assertEqual(db.data, [])
def test_DictDatabase(self):
db = livejson.DictDatabase(self.path)
self.assertTrue(os.path.exists(self.path))
self.assertEqual(db.data, {})
if __name__ == "__main__":
unittest.main()