source: pysyck/trunk/tests/test_parser.py @ 123

Revision 123, 9.5 KB checked in by xi, 8 years ago (diff)

Fix a leak in _syckmodule.c (thanks, jbj)

Change the production status to Stable.

RevLine 
[7]1
2import unittest
3
4import _syck
5
[123]6import StringIO, gc, sys
[7]7
8EXAMPLE = """
9-
10  avg: 0.278
11  hr: 65
12  name: Mark McGwire
13-
14  avg: 0.288
15  hr: 63
16  name: Sammy Sosa
17"""
18
19INVALID = """
20 - invalid
21- document
22""", 2, 0
23
24COMPARE1 = """
25one: foo
26two: bar
27three: baz
28""", {
29    'one': 'foo',
30    'two': 'bar',
31    'three': 'baz',
32}
33
34COMPARE2 = """
35- Mark McGwire
36- Sammy Sosa
37- Ken Griffey
38""", [
39    'Mark McGwire',
40    'Sammy Sosa',
41    'Ken Griffey',
42]
43
44COMPARE3 = """
45american:
46  - Boston Red Sox
47  - Detroit Tigers
48  - New York Yankees
49national:
50  - New York Mets
51  - Chicago Cubs
52  - Atlanta Braves
53""", {
54    'american': [
55        'Boston Red Sox',
56        'Detroit Tigers',
57        'New York Yankees',
58    ],
59    'national': [
60        'New York Mets',
61        'Chicago Cubs',
62        'Atlanta Braves',
63    ],
64}
65
66DOCUMENTS0 = ""
67
68DOCUMENTS1 = """
69---
70Time: 2001-11-23 15:01:42 -05:00
71User: ed
72Warning: >
73  This is an error message
74  for the log file
75"""
76
77DOCUMENTS2 = """
78---
79Time: 2001-11-23 15:01:42 -05:00
80User: ed
81Warning: >
82  This is an error message
83  for the log file
84---
85Time: 2001-11-23 15:02:31 -05:00
86User: ed
87Warning: >
88  A slightly different error
89  message.
90"""
91
92DOCUMENTS3 = """
93---
94Time: 2001-11-23 15:01:42 -05:00
95User: ed
96Warning: >
97  This is an error message
98  for the log file
99---
100Time: 2001-11-23 15:02:31 -05:00
101User: ed
102Warning: >
103  A slightly different error
104  message.
105---
106Date: 2001-11-23 15:03:17 -05:00
107User: ed
108Fatal: >
109  Unknown variable "bar"
110Stack:
111  - file: TopClass.py
112    line: 23
113    code: |
114      x = MoreObject("345\\n")
115  - file: MoreClass.py
116    line: 58
117    code: |-
118      foo = bar
119"""
120
121IMPLICIT_TYPING = """
122- 'foo'
123- >-
124  bar
125- baz
126- 123
127- 3.14
128- true
129- false
130- []
131- {}
132""", [
133    ('str', True),
134    ('str', True),
135    ('str', False),
136    ('int', False),
137    ('float#fix', False),
138    ('bool#yes', False),
139    ('bool#no', False),
140    (None, False),
141    (None, False),
142]
143
144EXPLICIT_TYPING = """
145- !int '123'
146- !yamltype 'foo'
147- !python/type 'bar'
148- !domain.tld,2002/type 'baz'
149- !!private 'private'
150- !map {}
151- !seq []
152""", [
153    'tag:yaml.org,2002:int',
154    'tag:yaml.org,2002:yamltype',
155    'tag:python.yaml.org,2002:type',
156    'tag:domain.tld,2002:type',
157    'x-private:private',
158    'tag:yaml.org,2002:map',
159    'tag:yaml.org,2002:seq',
160]
161
[17]162RECURSIVE = """
163--- &id002
164- &id001 Mark McGwire
165- Sammy Sosa
166- Ken Griffey
167- *id001
168- *id002
169"""
170
[20]171ALIASES = """
172- &alias foo
173- *alias
174"""
175
[123]176LEAKS = """
177- mere scalar
178- [ sequence, may, leak, too]
179- {"it's": mapping, with: many, potential: leaks}
180- {sequence: [], mapping: {}}
181"""
182
[15]183class TestAttributes(unittest.TestCase):
[7]184
[15]185    def testAttributes(self):
[7]186        parser = _syck.Parser(EXAMPLE)
[15]187        self.assertEqual(type(parser), _syck.Parser)
188        self.assertEqual(parser.source, EXAMPLE)
189        self.assertEqual(parser.implicit_typing, True)
190        self.assertEqual(parser.taguri_expansion, True)
191        self.assertEqual(parser.eof, False)
192        node = parser.parse()
193        self.assert_(isinstance(node, _syck.Node))
194        self.assertEqual(parser.source, EXAMPLE)
195        self.assertEqual(parser.implicit_typing, True)
196        self.assertEqual(parser.taguri_expansion, True)
197        self.assertEqual(parser.eof, False)
198        self.assertEqual(parser.parse(), None)
199        self.assertEqual(parser.eof, True)
[7]200
[15]201class TestGarbage(unittest.TestCase):
[7]202
[15]203    def testGarbage(self):
204        gc.collect()
205        source = []
206        parser = _syck.Parser(source)
207        source.append(parser)
208        del source, parser
209        self.assertEqual(gc.collect(), 2)
[7]210
211class TestErrors(unittest.TestCase):
212
213    def testError(self):
214        parser = _syck.Parser(INVALID[0])
[15]215        self.assertRaises(_syck.error, lambda: parser.parse())
[7]216
217    def testErrorLocation(self):
218        source, line, column = INVALID
219        parser = _syck.Parser(source)
220        try:
221            parser.parse()
222            raise Exception
223        except _syck.error, e:
224            self.assertEqual(e.args[1], line)
225            self.assertEqual(e.args[2], column)
226
227class EqualStructure:
228
229    def assertEqualStructure(self, node, structure):
230        if node.kind == 'scalar':
231            self.assertEqual(type(structure), str)
232            self.assertEqual(node.value, structure)
233        elif node.kind == 'seq':
234            self.assertEqual(type(structure), list)
235            self.assertEqual(len(node.value), len(structure))
[23]236            for i in range(len(node.value)):
237                item = node.value[i]
[7]238                self.assertEqualStructure(item, structure[i])
239        elif node.kind == 'map':
240            self.assertEqual(type(structure), dict)
241            self.assertEqual(len(node.value), len(structure))
242            for key in node.value:
243                self.assert_(key.value in structure)
244                self.assertEqualStructure(node.value[key], structure[key.value])
245
246class TestValuesAndSources(unittest.TestCase, EqualStructure):
247
248    def testValues1(self):
249        self._testValues(COMPARE1)
250
251    def testValues2(self):
252        self._testValues(COMPARE2)
253
254    def testValues3(self):
255        self._testValues(COMPARE3)
256
257    def testFileValues1(self):
258        self._testFileValues(COMPARE1)
259
260    def testFileValues2(self):
261        self._testFileValues(COMPARE2)
262
263    def testFileValues3(self):
264        self._testFileValues(COMPARE3)
265
266    def testNonsense(self):
267        parser = _syck.Parser(None)
[15]268        self.assertRaises(AttributeError, lambda: parser.parse())
[7]269
[15]270    def testCallMeNot(self):
271        class Source:
272            def __init__(self):
273                self.parser = None
274            def read(self, size=None):
275                if self.parser:
276                    self.parser.parse()
277                    return
278        source = Source()
279        parser = _syck.Parser(source)
280        source.parser = parser
281        self.assertRaises(RuntimeError, lambda: parser.parse())
282
283    def testAbsoluteHorror(self):
284        class Source:
285            def __init__(self):
286                self.parser = None
287            def read(self, size=None):
288                if self.parser:
289                    self.parser = None
290                return ''
291        source = Source()
292        parser = _syck.Parser(source)
293        source.parser = parser
294        del parser
295        self.assertEqual(None, source.parser.parse())
296
[7]297    def _testValues(self, (source, structure)):
298        parser = _syck.Parser(source)
299        document = parser.parse()
300        self.assertEqualStructure(document, structure)
301
302    def _testFileValues(self, (source, structure)):
303        parser = _syck.Parser(StringIO.StringIO(source))
304        document = parser.parse()
305        self.assertEqualStructure(document, structure)
306
307class TestDocuments(unittest.TestCase):
308
309    def testDocuments0(self):
310        self._testDocuments(DOCUMENTS0, 0)
311
312    def testDocuments1(self):
313        self._testDocuments(DOCUMENTS1, 1)
314
315    def testDocuments2(self):
316        self._testDocuments(DOCUMENTS2, 2)
317
318    def testDocuments3(self):
319        self._testDocuments(DOCUMENTS3, 3)
320
321    def _testDocuments(self, source, length):
322        parser = _syck.Parser(source)
323        actual_length = 0
324        while True:
325            document = parser.parse()
[15]326            if parser.eof:
[7]327                self.assertEqual(document, None)
328                break
329            actual_length += 1
330        self.assertEqual(actual_length, length)
331        self.assertEqual(parser.parse(), None)
[15]332        self.assert_(parser.eof)
[7]333        self.assertEqual(parser.parse(), None)
[15]334        self.assert_(parser.eof)
335        self.assert_(parser.eof)
[7]336
337class TestImplicitTyping(unittest.TestCase):
338
339    def testImplicitAndExpansionTyping(self):
340        self._testTyping(True, True)
341
342    def testImplicitTyping(self):
343        self._testTyping(True, False)
344
345    def testExpansionTyping(self):
346        self._testTyping(False, True)
347
348    def testNoTyping(self):
349        self._testTyping(False, False)
350
351    def _testTyping(self, implicit_typing, taguri_expansion):
[15]352        parser = _syck.Parser(IMPLICIT_TYPING[0], implicit_typing, taguri_expansion)
353        for node, (tag, explicit) in zip(parser.parse().value, IMPLICIT_TYPING[1]):
354            if tag is not None and taguri_expansion:
355                tag = 'tag:yaml.org,2002:%s' % tag
[7]356            if implicit_typing or explicit:
[15]357                self.assertEqual(node.tag, tag)
[7]358            else:
[15]359                self.assertEqual(node.tag, None)
[7]360
361class TestExplicitTyping(unittest.TestCase):
362
363    def testExplicitTyping(self):
364        parser = _syck.Parser(EXPLICIT_TYPING[0])
[15]365        for node, tag in zip(parser.parse().value, EXPLICIT_TYPING[1]):
366            self.assertEqual(node.tag, tag)
[7]367
[17]368class TestRecursive(unittest.TestCase):
369
370    def testRecursive(self):
371        parser = _syck.Parser(RECURSIVE)
372        self.assertRaises(TypeError, lambda: parser.parse())
373
[20]374class TestParsingAliases(unittest.TestCase):
375
376    def testAliases(self):
377        parser = _syck.Parser(ALIASES)
378        node = parser.parse()
379        self.assert_(node.value[0] is node.value[1])
380
[123]381class TestLeaks(unittest.TestCase):
382
383    def testLeaks(self):
384        parser = _syck.Parser(LEAKS)
385        node = parser.parse()
386        dummy = []
387        self.checkLeaks(node, dummy)
388
389    def checkLeaks(self, node, dummy):
390        self.assertEqual(sys.getrefcount(node), sys.getrefcount(dummy))
391        self.assertEqual(sys.getrefcount(node.value), 2)
392        dummy = []
393        container = [dummy]
394        if isinstance(node, _syck.Seq):
395            for item in node.value:
396                self.checkLeaks(item, dummy)
397        elif isinstance(node, _syck.Map):
398            for key in node.value:
399                self.checkLeaks(key, dummy)
400                value = node.value[key]
401                self.checkLeaks(value, dummy)
402
Note: See TracBrowser for help on using the repository browser.