chiark / gitweb /
@@@ remove debugging print
[mLib-python] / t / testutils.py
1 ### -*-python-*-
2 ###
3 ### Test utilities
4 ###
5 ### (c) 2019 Straylight/Edgeware
6 ###
7
8 ###----- Licensing notice ---------------------------------------------------
9 ###
10 ### This file is part of the Python interface to mLib.
11 ###
12 ### mLib/Python is free software: you can redistribute it and/or modify it
13 ### under the terms of the GNU General Public License as published by the
14 ### Free Software Foundation; either version 2 of the License, or (at your
15 ### option) any later version.
16 ###
17 ### mLib/Python is distributed in the hope that it will be useful, but
18 ### WITHOUT ANY WARRANTY; without even the implied warranty of
19 ### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20 ### General Public License for more details.
21 ###
22 ### You should have received a copy of the GNU General Public License
23 ### along with mLib/Python.  If not, write to the Free Software
24 ### Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
25 ### USA.
26
27 ###--------------------------------------------------------------------------
28 ### Imported modules.
29
30 import mLib as M
31 import contextlib as CTX
32 import os as OS
33 import sys as SYS
34 if SYS.version_info >= (3,): import builtins as B
35 else: import __builtin__ as B
36 import unittest as U
37
38 ###--------------------------------------------------------------------------
39 ### Main code.
40
41 ## Some compatibility hacks.
42 if SYS.version_info >= (3,):
43   PY2, PY3 = False, True
44   def bin(x): return x.encode('iso8859-1')
45   def py23(x, y): return y
46   range = range
47   byteseq = bytes
48   long = int
49   imap = map
50   def iterkeys(m): return m.keys()
51   def itervalues(m): return m.values()
52   def iteritems(m): return m.items()
53   from io import StringIO
54   MAXFIXNUM = SYS.maxsize
55 else:
56   import itertools as I
57   PY2, PY3 = True, False
58   def bin(x): return x
59   def py23(x, y): return x
60   range = xrange
61   long = long
62   imap = I.imap
63   def byteseq(seq): return "".join(map(chr, seq))
64   def iterkeys(m): return m.iterkeys()
65   def itervalues(m): return m.itervalues()
66   def iteritems(m): return m.iteritems()
67   from cStringIO import StringIO
68   MAXFIXNUM = SYS.maxint
69
70 def subprocess(fn):
71   def func(test):
72     for f in [SYS.stdout, SYS.stderr]: f.flush()
73     kid = OS.fork()
74     if kid == 0:
75       old_stderr_fd = OS.dup(2)
76       try: fn(test)
77       except:
78         for f in [SYS.stdout, SYS.stderr]: f.flush()
79         OS.dup2(old_stderr_fd, 2)
80         SYS.excepthook(*SYS.exc_info())
81         SYS.stderr.flush()
82         OS._exit(111)
83       else:
84         for f in [SYS.stdout, SYS.stderr]: f.flush()
85         OS._exit(0)
86     _, st = OS.waitpid(kid, 0)
87     if OS.WIFSIGNALED(st):
88       test.fail("child killed by signal %d" % OS.WTERMSIG(st))
89     elif not OS.WIFEXITED(st):
90       test.fail("child terminated with unknown status %x" % st)
91     else:
92       rc = OS.WEXITSTATUS(st)
93       if rc != 0: test.fail("child exited with status %d" % rc)
94   return func
95
96
97 class ImmutableMappingTextMixin (U.TestCase):
98
99   ## Subclass stubs.
100   def _mkkey(me, i): return "k#%d" % i
101   def _getkey(me, k): return int(k[2:])
102   def _getvalue(me, v): return int(v[2:])
103   def _getitem(me, it): k, v = it; return me._getkey(k), me._getvalue(v)
104
105   def check_immutable_mapping(me, map, model):
106
107     ## Lookup.
108     limk = 0
109     any = False
110     me.assertEqual(len(map), len(model))
111     for k, v in iteritems(model):
112       any = True
113       if k >= limk: limk = k + 1
114       me.assertTrue(me._mkkey(k) in map)
115       if PY2: me.assertTrue(map.has_key(me._mkkey(k)))
116       me.assertEqual(me._getvalue(map[me._mkkey(k)]), v)
117       me.assertEqual(me._getvalue(map.get(me._mkkey(k))), v)
118     if any: me.assertTrue(me._mkkey(k) in map)
119     if PY2: me.assertFalse(map.has_key(me._mkkey(limk)))
120     me.assertRaises(KeyError, lambda: map[me._mkkey(limk)])
121     me.assertEqual(map.get(me._mkkey(limk)), None)
122
123     if PY3:
124       empty = set()
125
126       for k, v in iteritems(map):
127         me.assertTrue(k in map.keys())
128         me.assertTrue((k, v) in map.items())
129       me.assertFalse(me._mkkey(limk) in map.keys())
130
131       for viewfn, getfn in [(lambda x: x.keys(), me._getkey),
132                             (lambda x: x.items(), me._getitem)]:
133         rview, rview2, mview = viewfn(map), viewfn(map), viewfn(model)
134         me.assertEqual(set(imap(getfn, rview)), set(mview))
135         me.assertEqual(rview, rview2)
136         me.assertEqual(rview, set(rview2))
137         me.assertEqual(rview | empty, set(rview))
138         me.assertEqual(rview | rview2, set(rview))
139         me.assertEqual(rview ^ empty, set(rview))
140         me.assertEqual(rview ^ rview, empty)
141         me.assertEqual(rview & empty, empty)
142         me.assertEqual(len(rview), len(model))
143
144         if any: subset = set(rview2); subset.pop()
145         superset = set(rview2); superset.add(object())
146
147         me.assertFalse(rview < rview2)
148         me.assertTrue(rview < superset)
149         me.assertFalse(superset < rview)
150         me.assertFalse(rview < empty)
151         if any:
152           me.assertTrue(empty < rview)
153           me.assertTrue(subset < rview)
154           me.assertFalse(rview < subset)
155
156         me.assertTrue(rview <= rview2)
157         me.assertTrue(rview <= superset)
158         me.assertFalse(superset <= rview)
159         if any:
160           me.assertTrue(empty <= rview)
161           me.assertFalse(rview <= empty)
162           me.assertTrue(subset <= rview)
163           me.assertFalse(rview <= subset)
164
165         me.assertTrue(rview >= rview2)
166         me.assertTrue(superset >= rview)
167         me.assertFalse(rview >= superset)
168         if any:
169           me.assertTrue(rview >= empty)
170           me.assertFalse(empty >= rview)
171           me.assertTrue(rview >= subset)
172           me.assertFalse(subset >= rview)
173
174         me.assertFalse(rview > rview2)
175         me.assertTrue(superset > rview)
176         me.assertFalse(rview > superset)
177         me.assertFalse(empty > rview)
178         if any:
179           me.assertTrue(rview > empty)
180           me.assertTrue(rview > subset)
181           me.assertFalse(subset > rview)
182
183     else:
184       for listfn, getfn in [(lambda x: x.keys(), me._getkey),
185                             (lambda x: x.values(), me._getvalue),
186                             (lambda x: x.items(), me._getitem)]:
187         rlist, mlist = listfn(map), listfn(model)
188         me.assertEqual(type(rlist), list)
189         rlist = B.map(getfn, rlist)
190         rlist.sort(); mlist.sort(); me.assertEqual(rlist, mlist)
191       for iterfn, getfn in [(lambda x: x.iterkeys(), me._getkey),
192                             (lambda x: x.itervalues(), me._getvalue),
193                             (lambda x: x.iteritems(), me._getitem)]:
194         me.assertEqual(set(imap(getfn, iterfn(map))), set(iterfn(model)))
195
196 class MutableMappingTestMixin (ImmutableMappingTextMixin):
197
198   ## Subclass stubs.
199   def _mkvalue(me, i): return "v#%d" % i
200
201   def check_mapping(me, emptymapfn):
202
203     map = emptymapfn()
204     me.assertEqual(len(map), 0)
205
206     if not PY3:
207       def check_views():
208         me.check_immutable_mapping(map, model)
209     else:
210       kview, iview, vview = map.keys(), map.items(), map.values()
211       def check_views():
212         me.check_immutable_mapping(map, model)
213         me.assertEqual(set(imap(me._getkey, kview)), model.keys())
214         me.assertEqual(set(imap(me._getitem, iview)), model.items())
215         me.assertEqual(set(imap(me._getvalue, vview)), set(model.values()))
216
217     model = { 1: 101, 2: 202, 4: 404 }
218     for k, v in iteritems(model): map[me._mkkey(k)] = me._mkvalue(v)
219     check_views()
220
221     model.update({ 2: 212, 6: 606, 7: 707 })
222     map.update({ me._mkkey(2): me._mkvalue(212),
223                  me._mkkey(6): me._mkvalue(606) },
224                **{ me._mkkey(7): me._mkvalue(707) })
225     check_views()
226
227     model[9] = 909
228     map[me._mkkey(9)] = me._mkvalue(909)
229     check_views()
230
231     model[9] = 919
232     map[me._mkkey(9)] = me._mkvalue(919)
233     check_views()
234
235     map.setdefault(me._mkkey(9), me._mkvalue(929))
236     check_views()
237
238     model[8] = 808
239     map.setdefault(me._mkkey(8), me._mkvalue(808))
240     check_views()
241
242     me.assertRaises(KeyError, map.pop, me._mkkey(5))
243     obj = object()
244     me.assertEqual(map.pop(me._mkkey(5), obj), obj)
245     me.assertEqual(me._getvalue(map.pop(me._mkkey(8))), 808)
246     del model[8]
247     check_views()
248
249     del model[9]
250     del map[me._mkkey(9)]
251     check_views()
252
253     k, v = map.popitem()
254     mk, mv = me._getkey(k), me._getvalue(v)
255     me.assertEqual(model[mk], mv)
256     del model[mk]
257     check_views()
258
259     map.clear()
260     model = {}
261     check_views()
262
263 ###----- That's all, folks --------------------------------------------------