scanner: Parse comments with */ not on a new line, but emit a warning
[platform/upstream/gobject-introspection.git] / tests / scanner / annotationparser / test_patterns.py
1 # -*- Mode: Python -*-
2 # GObject-Introspection - a framework for introspecting GObject libraries
3 # Copyright (C) 2012 Dieter Verfaillie <dieterv@optionexplicit.be>
4 #
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 # 02110-1301, USA.
19 #
20
21
22 '''
23 test_patterns.py
24
25 Tests ensuring the regular expression programs used
26 in annotationparser.py continue to function correctly.
27 Each regular expression program is tested on input that
28 should not match and input that should match. When input
29 should match, resulting symbolic groups are verified
30 against the expected output.
31 '''
32
33
34 from giscanner.annotationparser import (SECTION_RE, SYMBOL_RE, PROPERTY_RE,
35                                         SIGNAL_RE, PARAMETER_RE, TAG_RE,
36                                         COMMENT_END_RE)
37 from unittest import (TestCase, main)
38
39
40 identifier_section_tests = [
41     (SECTION_RE, 'TSIEOCN',
42          None),
43     (SECTION_RE, 'section',
44          None),
45     (SECTION_RE, 'section:',
46          None),
47     (SECTION_RE, 'section:test',
48          None),
49     (SECTION_RE, 'SECTION',
50          {'colon': '',
51           'section_name': None}),
52     (SECTION_RE, 'SECTION  \t   ',
53          {'colon': '',
54           'section_name': None}),
55     (SECTION_RE, '   \t  SECTION  \t   ',
56          {'colon': '',
57           'section_name': None}),
58     (SECTION_RE, 'SECTION:   \t ',
59          {'colon': ':',
60           'section_name': None}),
61     (SECTION_RE, 'SECTION   :   ',
62          {'colon': ':',
63           'section_name': None}),
64     (SECTION_RE, '   SECTION : ',
65          {'colon': ':',
66           'section_name': None}),
67     (SECTION_RE, 'SECTION:gtkwidget',
68          {'colon': ':',
69           'section_name': 'gtkwidget'}),
70     (SECTION_RE, 'SECTION:gtkwidget  ',
71          {'colon': ':',
72           'section_name': 'gtkwidget'}),
73     (SECTION_RE, '  SECTION:gtkwidget',
74          {'colon': ':',
75           'section_name': 'gtkwidget'}),
76     (SECTION_RE, '  SECTION:gtkwidget\t  ',
77          {'colon': ':',
78           'section_name': 'gtkwidget'}),
79     (SECTION_RE, 'SECTION:    gtkwidget   ',
80          {'colon': ':',
81           'section_name': 'gtkwidget'}),
82     (SECTION_RE, 'SECTION   :  gtkwidget',
83          {'colon': ':',
84           'section_name': 'gtkwidget'}),
85     (SECTION_RE, 'SECTION    gtkwidget \f  ',
86          {'colon': '',
87           'section_name': 'gtkwidget'})]
88
89 identifier_symbol_tests = [
90     (SYMBOL_RE, 'GBaseFinalizeFunc:',
91          {'colon': ':',
92           'symbol_name': 'GBaseFinalizeFunc',
93           'annotations': ''}),
94     (SYMBOL_RE, 'gtk_widget_show  ',
95          {'colon': '',
96           'symbol_name': 'gtk_widget_show',
97           'annotations': ''}),
98     (SYMBOL_RE, '  gtk_widget_show',
99          {'colon': '',
100           'symbol_name': 'gtk_widget_show',
101           'annotations': ''}),
102     (SYMBOL_RE, '  gtk_widget_show  ',
103          {'colon': '',
104           'symbol_name': 'gtk_widget_show',
105           'annotations': ''}),
106     (SYMBOL_RE, 'gtk_widget_show:',
107          {'colon': ':',
108           'symbol_name': 'gtk_widget_show',
109           'annotations': ''}),
110     (SYMBOL_RE, 'gtk_widget_show :',
111          {'colon': ':',
112           'symbol_name': 'gtk_widget_show',
113           'annotations': ''}),
114     (SYMBOL_RE, 'gtk_widget_show:  ',
115          {'colon': ':',
116           'symbol_name': 'gtk_widget_show',
117           'annotations': ''}),
118     (SYMBOL_RE, 'gtk_widget_show :  ',
119          {'colon': ':',
120           'symbol_name': 'gtk_widget_show',
121           'annotations': ''}),
122     (SYMBOL_RE, '  gtk_widget_show:',
123          {'colon': ':',
124           'symbol_name': 'gtk_widget_show',
125           'annotations': ''}),
126     (SYMBOL_RE, '  gtk_widget_show :',
127          {'colon': ':',
128           'symbol_name': 'gtk_widget_show',
129           'annotations': ''}),
130     (SYMBOL_RE, '  gtk_widget_show:  ',
131          {'colon': ':',
132           'symbol_name': 'gtk_widget_show',
133           'annotations': ''}),
134     (SYMBOL_RE, '  gtk_widget_show :  ',
135          {'colon': ':',
136           'symbol_name': 'gtk_widget_show',
137           'annotations': ''}),
138     (SYMBOL_RE, 'gtk_widget_show (skip)',
139          {'colon': '',
140           'symbol_name': 'gtk_widget_show',
141           'annotations': '(skip)'}),
142     (SYMBOL_RE, 'gtk_widget_show: (skip)',
143          {'colon': ':',
144           'symbol_name': 'gtk_widget_show',
145           'annotations': '(skip)'}),
146     (SYMBOL_RE, 'gtk_widget_show : (skip)',
147          {'colon': ':',
148           'symbol_name': 'gtk_widget_show',
149           'annotations': '(skip)'}),
150     (SYMBOL_RE, 'gtk_widget_show:  (skip)',
151          {'colon': ':',
152           'symbol_name': 'gtk_widget_show',
153           'annotations': '(skip)'}),
154     (SYMBOL_RE, 'gtk_widget_show :  (skip)',
155          {'colon': ':',
156           'symbol_name': 'gtk_widget_show',
157           'annotations': '(skip)'}),
158     (SYMBOL_RE, '  gtk_widget_show:(skip)',
159          {'colon': ':',
160           'symbol_name': 'gtk_widget_show',
161           'annotations': '(skip)'}),
162     (SYMBOL_RE, '  gtk_widget_show :(skip)',
163          {'colon': ':',
164           'symbol_name': 'gtk_widget_show',
165           'annotations': '(skip)'}),
166     (SYMBOL_RE, '  gtk_widget_show:  (skip)',
167          {'colon': ':',
168           'symbol_name': 'gtk_widget_show',
169           'annotations': '(skip)'}),
170     (SYMBOL_RE, '  gtk_widget_show :  (skip)    \t    ',
171          {'colon': ':',
172           'symbol_name': 'gtk_widget_show',
173           'annotations': '(skip)    \t    '}),
174     (SYMBOL_RE, '  gtk_widget_show  :  (skip)   \t    ',
175          {'colon': ':',
176           'symbol_name': 'gtk_widget_show',
177           'annotations': '(skip)   \t    '}),
178     (SYMBOL_RE, 'gtk_widget_show:(skip)(test1)',
179          {'colon': ':',
180           'symbol_name': 'gtk_widget_show',
181           'annotations': '(skip)(test1)'}),
182     (SYMBOL_RE, 'gtk_widget_show (skip)(test1)',
183          {'colon': '',
184           'symbol_name': 'gtk_widget_show',
185           'annotations': '(skip)(test1)'}),
186     (SYMBOL_RE, 'gtk_widget_show: (skip) (test1)',
187          {'colon': ':',
188           'symbol_name': 'gtk_widget_show',
189           'annotations': '(skip) (test1)'}),
190     (SYMBOL_RE, 'gtk_widget_show : (skip) (test1)',
191          {'colon': ':',
192           'symbol_name': 'gtk_widget_show',
193           'annotations': '(skip) (test1)'}),
194     (SYMBOL_RE, 'gtk_widget_show:  (skip) (test1)',
195          {'colon': ':',
196           'symbol_name': 'gtk_widget_show',
197           'annotations': '(skip) (test1)'}),
198     (SYMBOL_RE, 'gtk_widget_show :  (skip) (test1)',
199          {'colon': ':',
200           'symbol_name': 'gtk_widget_show',
201           'annotations': '(skip) (test1)'}),
202     (SYMBOL_RE, '  gtk_widget_show:(skip) (test1)',
203          {'colon': ':',
204           'symbol_name': 'gtk_widget_show',
205           'annotations': '(skip) (test1)'}),
206     (SYMBOL_RE, '  gtk_widget_show :(skip) (test1)',
207          {'colon': ':',
208           'symbol_name': 'gtk_widget_show',
209           'annotations': '(skip) (test1)'}),
210     (SYMBOL_RE, '  gtk_widget_show:  (skip) (test1)',
211          {'colon': ':',
212           'symbol_name': 'gtk_widget_show',
213           'annotations': '(skip) (test1)'}),
214     (SYMBOL_RE, '  gtk_widget_show :  (skip) (test1)  ',
215          {'colon': ':',
216           'symbol_name': 'gtk_widget_show',
217           'annotations': '(skip) (test1)  '}),
218     (SYMBOL_RE, 'gtk_widget_show: (skip) (test1) (test-2)',
219          {'colon': ':',
220           'symbol_name': 'gtk_widget_show',
221           'annotations': '(skip) (test1) (test-2)'}),
222     (SYMBOL_RE, 'gtk_widget_show : (skip) (test1) (test-2)',
223          {'colon': ':',
224           'symbol_name': 'gtk_widget_show',
225           'annotations': '(skip) (test1) (test-2)'}),
226     (SYMBOL_RE, 'gtk_widget_show:  (skip) (test1) (test-2)',
227          {'colon': ':',
228           'symbol_name': 'gtk_widget_show',
229           'annotations': '(skip) (test1) (test-2)'}),
230     (SYMBOL_RE, 'gtk_widget_show :  (skip) (test1) (test-2)',
231          {'colon': ':',
232           'symbol_name': 'gtk_widget_show',
233           'annotations': '(skip) (test1) (test-2)'}),
234     (SYMBOL_RE, '  gtk_widget_show:(skip) (test1) (test-2)',
235          {'colon': ':',
236           'symbol_name': 'gtk_widget_show',
237           'annotations': '(skip) (test1) (test-2)'}),
238     (SYMBOL_RE, '  gtk_widget_show :(skip) (test1) (test-2)',
239          {'colon': ':',
240           'symbol_name': 'gtk_widget_show',
241           'annotations': '(skip) (test1) (test-2)'}),
242     (SYMBOL_RE, '  gtk_widget_show:  (skip) (test1) (test-2)',
243          {'colon': ':',
244           'symbol_name': 'gtk_widget_show',
245           'annotations': '(skip) (test1) (test-2)'}),
246     (SYMBOL_RE, '  gtk_widget_show :  (skip) (test1) (test-2)  ',
247          {'colon': ':',
248           'symbol_name': 'gtk_widget_show',
249           'annotations': '(skip) (test1) (test-2)  '}),
250     (SYMBOL_RE, '  gtk_widget_show  :  (skip)  (test1)  (test-2)  ',
251          {'colon': ':',
252           'symbol_name': 'gtk_widget_show',
253           'annotations': '(skip)  (test1)  (test-2)  '}),
254     # constants
255     (SYMBOL_RE, 'MY_CONSTANT:',
256          {'colon': ':',
257           'symbol_name': 'MY_CONSTANT',
258           'annotations': ''}),
259     # structs
260     (SYMBOL_RE, 'FooWidget:',
261          {'colon': ':',
262           'symbol_name': 'FooWidget',
263           'annotations': ''}),
264     # enums
265     (SYMBOL_RE, 'Something:',
266          {'colon': ':',
267           'symbol_name': 'Something',
268           'annotations': ''})]
269
270 identifier_property_tests = [
271     # simple property name
272     (PROPERTY_RE, 'GtkWidget:name (skip)',
273          {'class_name': 'GtkWidget',
274           'property_name': 'name',
275           'colon': '',
276           'annotations': '(skip)'}),
277     (PROPERTY_RE, 'GtkWidget:name',
278          {'class_name': 'GtkWidget',
279           'property_name': 'name',
280           'colon': '',
281           'annotations': ''}),
282     (PROPERTY_RE, ' GtkWidget :name',
283          {'class_name': 'GtkWidget',
284           'property_name': 'name',
285           'colon': '',
286           'annotations': ''}),
287     (PROPERTY_RE, 'GtkWidget: name ',
288          {'class_name': 'GtkWidget',
289           'property_name': 'name',
290           'colon': '',
291           'annotations': ''}),
292     (PROPERTY_RE, '  GtkWidget  :  name  ',
293          {'class_name': 'GtkWidget',
294           'property_name': 'name',
295           'colon': '',
296           'annotations': ''}),
297     (PROPERTY_RE, 'GtkWidget:name:',
298          {'class_name': 'GtkWidget',
299           'property_name': 'name',
300           'colon': ':',
301           'annotations': ''}),
302     (PROPERTY_RE, 'GtkWidget:name:  ',
303          {'class_name': 'GtkWidget',
304           'property_name': 'name',
305           'colon': ':',
306           'annotations': ''}),
307     (PROPERTY_RE, '  GtkWidget:name:',
308          {'class_name': 'GtkWidget',
309           'property_name': 'name',
310           'colon': ':',
311           'annotations': ''}),
312     (PROPERTY_RE, 'Something:name:',
313          {'class_name': 'Something',
314           'property_name': 'name',
315           'colon': ':',
316           'annotations': ''}),
317     (PROPERTY_RE, 'Something:name:  ',
318          {'class_name': 'Something',
319           'property_name': 'name',
320           'colon': ':',
321           'annotations': ''}),
322     (PROPERTY_RE, '  Something:name:',
323          {'class_name': 'Something',
324           'property_name': 'name',
325           'colon': ':',
326           'annotations': ''}),
327     (PROPERTY_RE, 'Weird-thing:name:',
328          None),
329     (PROPERTY_RE, 'really-weird_thing:name:',
330          None),
331     (PROPERTY_RE, 'GWin32InputStream:handle:',
332          {'class_name': 'GWin32InputStream',
333           'property_name': 'handle',
334           'colon': ':',
335           'annotations': ''}),
336     # property name that contains a dash
337     (PROPERTY_RE, 'GtkWidget:double-buffered (skip)',
338          {'class_name': 'GtkWidget',
339           'property_name': 'double-buffered',
340           'colon': '',
341           'annotations': '(skip)'}),
342     (PROPERTY_RE, 'GtkWidget:double-buffered',
343          {'class_name': 'GtkWidget',
344           'property_name': 'double-buffered',
345           'colon': '',
346           'annotations': ''}),
347     (PROPERTY_RE, ' GtkWidget :double-buffered',
348          {'class_name': 'GtkWidget',
349           'property_name': 'double-buffered',
350           'colon': '',
351           'annotations': ''}),
352     (PROPERTY_RE, 'GtkWidget: double-buffered ',
353          {'class_name': 'GtkWidget',
354           'property_name': 'double-buffered',
355           'colon': '',
356           'annotations': ''}),
357     (PROPERTY_RE, '  GtkWidget  :  double-buffered  ',
358          {'class_name': 'GtkWidget',
359           'property_name': 'double-buffered',
360           'colon': '',
361           'annotations': ''}),
362     (PROPERTY_RE, 'GtkWidget:double-buffered:',
363          {'class_name': 'GtkWidget',
364           'property_name': 'double-buffered',
365           'colon': ':',
366           'annotations': ''}),
367     (PROPERTY_RE, 'GtkWidget:double-buffered:  ',
368          {'class_name': 'GtkWidget',
369           'property_name': 'double-buffered',
370           'colon': ':',
371           'annotations': ''}),
372     (PROPERTY_RE, '  GtkWidget:double-buffered:',
373          {'class_name': 'GtkWidget',
374           'property_name': 'double-buffered',
375           'colon': ':',
376           'annotations': ''}),
377     (PROPERTY_RE, 'Something:double-buffered:',
378          {'class_name': 'Something',
379           'property_name': 'double-buffered',
380           'colon': ':',
381           'annotations': ''}),
382     (PROPERTY_RE, 'Something:double-buffered:  ',
383          {'class_name': 'Something',
384           'property_name': 'double-buffered',
385           'colon': ':',
386           'annotations': ''}),
387     (PROPERTY_RE, '  Something:double-buffered:',
388          {'class_name': 'Something',
389           'property_name': 'double-buffered',
390           'colon': ':',
391           'annotations': ''}),
392     (PROPERTY_RE, 'Weird-thing:double-buffered:',
393          None),
394     (PROPERTY_RE, 'really-weird_thing:double-buffered:',
395          None),
396     (PROPERTY_RE, ' GMemoryOutputStream:realloc-function: (skip)',
397          {'class_name': 'GMemoryOutputStream',
398           'property_name': 'realloc-function',
399           'colon': ':',
400           'annotations': '(skip)'})]
401
402 identifier_signal_tests = [
403     # simple property name
404     (SIGNAL_RE, 'GtkWidget::changed: (skip)',
405          {'class_name': 'GtkWidget',
406           'signal_name': 'changed',
407           'colon': ':',
408           'annotations': '(skip)'}),
409     (SIGNAL_RE, 'GtkWidget::changed:',
410          {'class_name': 'GtkWidget',
411           'signal_name': 'changed',
412           'colon': ':',
413           'annotations': ''}),
414     (SIGNAL_RE, 'Something::changed:',
415          {'class_name': 'Something',
416           'signal_name': 'changed',
417           'colon': ':',
418           'annotations': ''}),
419     (SIGNAL_RE, 'Weird-thing::changed:',
420          None),
421     (SIGNAL_RE, 'really-weird_thing::changed:',
422          None),
423     # signal name that contains a dash
424     (SIGNAL_RE, 'GtkWidget::hierarchy-changed: (skip)',
425          {'class_name': 'GtkWidget',
426           'signal_name': 'hierarchy-changed',
427           'colon': ':',
428           'annotations': '(skip)'}),
429     (SIGNAL_RE, 'GtkWidget::hierarchy-changed:',
430          {'class_name': 'GtkWidget',
431           'signal_name': 'hierarchy-changed',
432           'colon': ':',
433           'annotations': ''}),
434     (SIGNAL_RE, 'Something::hierarchy-changed:',
435          {'class_name': 'Something',
436           'signal_name': 'hierarchy-changed',
437           'colon': ':',
438           'annotations': ''}),
439     (SIGNAL_RE, 'Weird-thing::hierarchy-changed:',
440          None),
441     (SIGNAL_RE, 'really-weird_thing::hierarchy-changed:',
442          None)]
443
444 parameter_tests = [
445     (PARAMETER_RE, '@Short_description: Base class for all widgets  ',
446          {'parameter_name': 'Short_description',
447           'annotations': '',
448           'colon': '',
449           'description': 'Base class for all widgets'}),
450     (PARAMETER_RE, '@...: the value of the first property, followed optionally by more',
451          {'parameter_name': '...',
452           'annotations': '',
453           'colon': '',
454           'description': 'the value of the first property, followed optionally by more'}),
455     (PARAMETER_RE, '@widget: a #GtkWidget',
456          {'parameter_name': 'widget',
457           'annotations': '',
458           'colon': '',
459           'description': 'a #GtkWidget'}),
460     (PARAMETER_RE, '@widget_pointer: (inout) (transfer none): '
461                    'address of a variable that contains @widget',
462          {'parameter_name': 'widget_pointer',
463           'annotations': '(inout) (transfer none)',
464           'colon': ':',
465           'description': 'address of a variable that contains @widget'}),
466     (PARAMETER_RE, '@weird_thing: (inout) (transfer none) (allow-none) (attribute) (destroy) '
467                    '(foreign) (inout) (out) (transfer) (skip) (method): some weird @thing',
468          {'parameter_name': 'weird_thing',
469           'annotations': '(inout) (transfer none) (allow-none) (attribute) (destroy) '
470                             '(foreign) (inout) (out) (transfer) (skip) (method)',
471           'colon': ':',
472           'description': 'some weird @thing'}),
473     (PARAMETER_RE, '@data: a pointer to the element data. The data may be moved as elements '
474                    'are added to the #GByteArray.',
475          {'parameter_name': 'data',
476           'annotations': '',
477           'colon': '',
478           'description': 'a pointer to the element data. The data may be moved as elements '
479                             'are added to the #GByteArray.'}),
480     (PARAMETER_RE, '@a: a #GSequenceIter',
481          {'parameter_name': 'a',
482           'annotations': '',
483           'colon': '',
484           'description': 'a #GSequenceIter'}),
485     (PARAMETER_RE, '@keys: (array length=n_keys) (element-type GQuark) (allow-none):',
486          {'parameter_name': 'keys',
487           'annotations': '(array length=n_keys) (element-type GQuark) (allow-none)',
488           'colon': ':',
489           'description': ''})]
490
491 tag_tests = [
492     (TAG_RE, 'Since 3.0',
493          None),
494     (TAG_RE, 'Since: 3.0',
495          {'tag_name': 'Since',
496           'annotations': '',
497           'colon': '',
498           'description': '3.0'}),
499     (TAG_RE, 'Attributes: (inout) (transfer none): some note about attributes',
500          {'tag_name': 'Attributes',
501           'annotations': '(inout) (transfer none)',
502           'colon': ':',
503           'description': 'some note about attributes'}),
504     (TAG_RE, 'Rename to: something_else',
505          {'tag_name': 'Rename to',
506           'annotations': '',
507           'colon': '',
508           'description': 'something_else'}),
509     (TAG_RE, '@Deprecated: Since 2.8, reference counting is done atomically',
510          None),
511     (TAG_RE, 'Returns %TRUE and does weird things',
512          None),
513     (TAG_RE, 'Returns: a #GtkWidget',
514          {'tag_name': 'Returns',
515           'annotations': '',
516           'colon': '',
517           'description': 'a #GtkWidget'}),
518     (TAG_RE, 'Return value: (transfer none): The binary data that @text responds. '
519              'This pointer',
520          {'tag_name': 'Return value',
521           'annotations': '(transfer none)',
522           'colon': ':',
523           'description': 'The binary data that @text responds. This pointer'}),
524     (TAG_RE, 'Return value: (transfer full) (array length=out_len) (element-type guint8):',
525          {'tag_name': 'Return value',
526           'annotations': '(transfer full) (array length=out_len) (element-type guint8)',
527           'colon': ':',
528           'description': ''}),
529     (TAG_RE, 'Returns: A boolean value, but let me tell you a bit about this boolean.  It',
530          {'tag_name': 'Returns',
531           'annotations': '',
532           'colon': '',
533           'description': 'A boolean value, but let me tell you a bit about this boolean.  '
534                          'It'}),
535     (TAG_RE, 'Returns: (transfer container) (element-type GObject.ParamSpec): a',
536          {'tag_name': 'Returns',
537           'annotations': '(transfer container) (element-type GObject.ParamSpec)',
538           'colon': ':',
539           'description': 'a'}),
540     (TAG_RE, 'Return value: (type GLib.HashTable<utf8,GLib.HashTable<utf8,utf8>>) '
541              '(transfer full):',
542          {'tag_name': 'Return value',
543           'annotations': '(type GLib.HashTable<utf8,GLib.HashTable<utf8,utf8>>) '
544                          '(transfer full)',
545           'colon': ':',
546           'description': ''})]
547
548 comment_end_tests = [
549     (COMMENT_END_RE, '*/',
550          {'description': ''}),
551     (COMMENT_END_RE, '   */',
552          {'description': ''}),
553     (COMMENT_END_RE, ' */ ',
554          {'description': ''}),
555     (COMMENT_END_RE, '**/',
556          {'description': ''}),
557     (COMMENT_END_RE, ' **/',
558          {'description': ''}),
559     (COMMENT_END_RE, ' **/ ',
560          {'description': ''}),
561     (COMMENT_END_RE, 'test */',
562          {'description': 'test'}),
563     (COMMENT_END_RE, ' test*/',
564          {'description': 'test'}),
565     (COMMENT_END_RE, 'test **/',
566          {'description': 'test'}),
567     (COMMENT_END_RE, ' test**/',
568          {'description': 'test'}),
569     (COMMENT_END_RE, 'test *****/',
570          {'description': 'test'}),
571     (COMMENT_END_RE, ' test*****/',
572          {'description': 'test'})]
573
574
575 def create_tests(tests_name, testcases):
576     for (index, testcase) in enumerate(testcases):
577         real_test_name = '%s_%03d' % (tests_name, index)
578
579         test_method = TestProgram.__create_test__(testcase)
580         test_method.__name__ = real_test_name
581         setattr(TestProgram, real_test_name, test_method)
582
583
584 class TestProgram(TestCase):
585     @classmethod
586     def __create_test__(cls, testcase):
587         def do_test(self):
588             (program, text, expected) = testcase
589
590             match = program.match(text)
591
592             if expected is None:
593                 msg = 'Program matched text but shouldn\'t:\n"%s"'
594                 self.assertTrue(match is None, msg % (text, ))
595             else:
596                 msg = 'Program should match text but didn\'t:\n"%s"'
597                 self.assertTrue(match is not None, msg % (text, ))
598
599                 for key, value in expected.items():
600                     msg = 'expected "%s" for "%s" but match returned "%s"'
601                     msg = msg % (value, key, match.group(key))
602                     self.assertEqual(match.group(key), value, msg)
603
604         return do_test
605
606
607 if __name__ == '__main__':
608     # Create tests from data
609     create_tests('test_identifier_section', identifier_section_tests)
610     create_tests('test_identifier_symbol', identifier_symbol_tests)
611     create_tests('test_identifier_property', identifier_property_tests)
612     create_tests('test_identifier_signal', identifier_signal_tests)
613     create_tests('test_parameter', parameter_tests)
614     create_tests('test_tag', tag_tests)
615     create_tests('test_comment_end', comment_end_tests)
616
617     # Run test suite
618     main()