Contact: fumanchu@aminus.org

Log in as guest/misc to create tickets

root/test_pyconquer.py

Revision 96 (checked in by fumanchu, 6 years ago)

pyconquer cleanups. To reduce right-shift of output, swimlane indexes now get re-used in Logger once their thread terminates.

  • Property svn:eol-style set to native
Line 
1 """Test pyconquer.py"""
2
3 import os
4 localDir = os.path.dirname(__file__)
5 import pprint
6 try:
7     from cStringIO import StringIO
8 except ImportError:
9     from StringIO import StringIO
10 import sys
11 import threading
12 import unittest
13
14 import pyconquer
15
16
17 class Counter:
18     def __init__(self):
19         self.value = 0
20
21 def plus1(arg):
22     newval = arg + 1
23     return newval
24
25
26 class LoggerTests(unittest.TestCase):
27    
28     def testNormalFlow(self):
29         c = Counter()
30        
31         tr = pyconquer.Logger("!(threading.py)")
32         self.assertEqual(tr.events, set(['call', 'return', 'exception']))
33         tr.watch(c, "value", name='c.value')
34         tr.out = StringIO()
35         try:
36             tr.start()
37             def inc():
38                 c.value += 1
39             for i in xrange(3):
40                 threading.Thread(target=inc).start()
41         finally:
42             tr.stop()
43        
44         self.assertEqual(c.value, 3)
45         self.assertEqual(tr.out.getvalue(), """
46
47 ________ pyconquer tracing started ________
48
49 = c.value: 0
50
51 [ MainThread ]
52 * testNormalFlow (test_pyconquer:%(a)s) New Thread: Thread-1
53
54         [ Thread-1 ]
55         > inc (test_pyconquer:%(b)s)
56         < inc (test_pyconquer:%(c)s)
57         = c.value: 1
58         X Thread terminated: Thread-1
59
60 [ MainThread ]
61 * testNormalFlow (test_pyconquer:%(a)s) New Thread: Thread-2
62
63         [ Thread-2 ]
64         > inc (test_pyconquer:%(b)s)
65         < inc (test_pyconquer:%(c)s)
66         = c.value: 2
67         X Thread terminated: Thread-2
68
69 [ MainThread ]
70 * testNormalFlow (test_pyconquer:%(a)s) New Thread: Thread-3
71
72         [ Thread-3 ]
73         > inc (test_pyconquer:%(b)s)
74         < inc (test_pyconquer:%(c)s)
75         = c.value: 3
76         X Thread terminated: Thread-3
77
78 ________ pyconquer tracing stopped ________
79 """ % {'a': tr._caller_lineno + 4,
80        'b': tr._caller_lineno + 1,
81        'c': tr._caller_lineno + 2})
82        
83         # Test 1) a start() after stop(),
84         #      2) lines in this frame, and
85         #      3) a call inside a line
86         tr.events.add('line')
87         tr.watches.clear()
88         tr.out = StringIO()
89         try:
90             tr.start()
91             c.value = 0
92             c.value = plus1(plus1(1))
93         finally:
94             tr.stop()
95        
96         self.assertEqual(c.value, 3)
97         self.assertEqual(tr.out.getvalue(), """
98
99 ________ pyconquer tracing started ________
100
101
102 [ MainThread ]
103   testNormalFlow (test_pyconquer:%(x)s)
104   testNormalFlow (test_pyconquer:%(y)s)
105 > plus1 (test_pyconquer:%(a)s)
106   plus1 (test_pyconquer:%(b)s)
107   plus1 (test_pyconquer:%(c)s)
108 < plus1 (test_pyconquer:%(c)s): 2
109 > plus1 (test_pyconquer:%(a)s)
110   plus1 (test_pyconquer:%(b)s)
111   plus1 (test_pyconquer:%(c)s)
112 < plus1 (test_pyconquer:%(c)s): 3
113   testNormalFlow (test_pyconquer:%(z)s)
114
115 ________ pyconquer tracing stopped ________
116 """ % {'a': 21, 'b': 22, 'c': 23,
117        'x': tr._caller_lineno + 1,
118        'y': tr._caller_lineno + 2,
119        'z': tr._caller_lineno + 4})
120    
121     def testCEvents(self):
122         c = Counter()
123         tr = pyconquer.Logger(events=('call', 'return', 'exception',
124                                       'c_call', 'c_return', 'c_exception'))
125         tr.out = StringIO()
126         try:
127             tr.start()
128             # Trace c_call and c_return
129             c.value = sys.getrecursionlimit()
130             # Raise a c_exception
131             try:
132                 c.value = chr('a')
133             except TypeError:
134                 pass
135         finally:
136             tr.stop()
137        
138         self.assertEqual(tr.out.getvalue(), """
139
140 ________ pyconquer tracing started ________
141
142
143 [ MainThread ]
144 >>testCEvents (test_pyconquer:%(a)s): <built-in function getrecursionlimit>
145 <<testCEvents (test_pyconquer:%(a)s): <built-in function getrecursionlimit>
146 >>testCEvents (test_pyconquer:%(b)s): <built-in function chr>
147 EEtestCEvents (test_pyconquer:%(b)s): <built-in function chr>
148 E testCEvents (test_pyconquer:%(b)s): TypeError
149
150 ________ pyconquer tracing stopped ________
151 """ % {'a': tr._caller_lineno + 2,
152        'b': tr._caller_lineno + 5})
153
154
155 class VerifierTests(unittest.TestCase):
156    
157     def test_0_Tree(self):
158         pt = pyconquer.Verifier()
159        
160         def three():
161             return 3
162         pt.compile(three)
163        
164         # Assert the first four Nodes.
165         level1 = pt.tree.children
166         self.assertEqual(len(level1), 2)
167         self.assertEqual(len(level1[0].children), 2)
168         self.assertEqual(len(level1[1].children), 2)
169        
170         def a_is_1():
171             a = 1
172             b = a
173         pt.compile(a_is_1)
174        
175         # Assert the first four Nodes.
176         level1 = pt.tree.children
177         self.assertEqual(len(level1), 2)
178         self.assertEqual(len(level1[0].children), 2)
179         self.assertEqual(len(level1[1].children), 2)
180    
181     def test_1_SequenceTests(self):
182         c = Counter()
183         a = [c.value]
184         def test():
185             a[0] += 2 # One for each thread!
186             return c.value == a[0]
187        
188         pt = pyconquer.Verifier()
189 ##        pt = pyconquer.Verifier(pyconquer.all_events)
190        
191         # Test a "safe" increment function. This isn't truly safe,
192         # because Python threads may switch even between the
193         # bytecodes LOAD_ATTR (value), and LOAD_CONST (1).
194         # [But LOAD_CONST should "goto fast_next_opcode" on its
195         # way to INPLACE_ADD and skip releasing the GIL].
196         # But it passes our test. ;)
197         def safe_inc():
198             c.value += 1
199         try:
200             pt.compile(safe_inc)
201         except pyconquer.VerificationError, x:
202             print
203             for node in pt.trail:
204                 print node
205             print "TREE:", pt.tree
206             self.fail("safe_inc failed")
207
208
209 if __name__ == '__main__':
210     unittest.main()
211
Note: See TracBrowser for help on using the browser.