Merge lp:~justas.sadzevicius/schooltool/schooltool.pdf_testing into lp:~schooltool-owners/schooltool/schooltool
- schooltool.pdf_testing
- Merge into schooltool
Proposed by
Justas Sadzevičius
Status: | Merged |
---|---|
Merge reported by: | Ignas Mikalajūnas |
Merged at revision: | not available |
Proposed branch: | lp:~justas.sadzevicius/schooltool/schooltool.pdf_testing |
Merge into: | lp:~schooltool-owners/schooltool/schooltool |
Diff against target: | None lines |
To merge this branch: | bzr merge lp:~justas.sadzevicius/schooltool/schooltool.pdf_testing |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ignas Mikalajūnas (community) | Approve | ||
Review via email: mp+5026@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
Justas Sadzevičius (justas.sadzevicius) wrote : | # |
- 2467. By Justas Sadzevičius
-
Refactored helpers to a single StoryXML class.
- 2468. By Justas Sadzevičius
-
Whitespace
- 2469. By Justas Sadzevičius
-
Improved readability of README.txt and test_pdf a bit.
Revision history for this message
Ignas Mikalajūnas (ignas) : | # |
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'src/schooltool/testing/README.txt' |
2 | --- src/schooltool/testing/README.txt 2008-01-15 18:46:51 +0000 |
3 | +++ src/schooltool/testing/README.txt 2009-03-30 12:24:08 +0000 |
4 | @@ -161,3 +161,60 @@ |
5 | >>> print analyze.queryHTML('/html/body/h1', html)[0] |
6 | <h1>This is my page!</h1> |
7 | |
8 | + |
9 | +Reportlab PDF story testing |
10 | +--------------------------- |
11 | + |
12 | +Schooltool PDF reports utilize Reportlab platypus module. A report is |
13 | +built from a list of platypus flowables known as as 'story'. |
14 | + |
15 | +Let's build a short pdf story. |
16 | + |
17 | + >>> from reportlab.lib.styles import ParagraphStyle |
18 | + >>> from reportlab.platypus.paragraph import Paragraph |
19 | + >>> from reportlab.platypus.flowables import PageBreak |
20 | + |
21 | + >>> style = ParagraphStyle(name='Test', fontName='Times-Roman') |
22 | + |
23 | + >>> story = [ |
24 | + ... Paragraph('Hello world', style), |
25 | + ... PageBreak(), |
26 | + ... Paragraph('A new page', style)] |
27 | + |
28 | +There are several helpers for testing the stories. |
29 | + |
30 | + >>> from schooltool.testing import pdf |
31 | + |
32 | +The tools aim to build a human readable XML representation of the |
33 | +story. There is a helper which prints the formatted XML: |
34 | + |
35 | + >>> pdf.printStoryXML(story) |
36 | + <story> |
37 | + <Paragraph>Hello world</Paragraph> |
38 | + <PageBreak/> |
39 | + <Paragraph>A new page</Paragraph> |
40 | + </story> |
41 | + |
42 | +As with HTML analyzation tools, there are helpers for XPath queries: |
43 | + |
44 | + >>> pdf.queryStory('//Paragraph', story) |
45 | + ['<Paragraph>Hello world</Paragraph>', |
46 | + '<Paragraph>A new page</Paragraph>'] |
47 | + |
48 | + >>> pdf.printQuery('//Paragraph', story) |
49 | + <Paragraph>Hello world</Paragraph> |
50 | + <Paragraph>A new page</Paragraph> |
51 | + |
52 | +These helpers also work on single platypus flowables: |
53 | + |
54 | + >>> pdf.printStoryXML(Paragraph('Some text', style)) |
55 | + <story> |
56 | + <Paragraph>Some text</Paragraph> |
57 | + </story> |
58 | + |
59 | +If these helpers are not sufficient, we can build the raw XML document. |
60 | + |
61 | + >>> document = pdf.getStoryXML(story) |
62 | + >>> document |
63 | + <...ElementTree object ...> |
64 | + |
65 | |
66 | === added file 'src/schooltool/testing/pdf.py' |
67 | --- src/schooltool/testing/pdf.py 1970-01-01 00:00:00 +0000 |
68 | +++ src/schooltool/testing/pdf.py 2009-03-30 11:49:24 +0000 |
69 | @@ -0,0 +1,240 @@ |
70 | +# |
71 | +# SchoolTool - common information systems platform for school administration |
72 | +# Copyright (c) 2005 Shuttleworth Foundation |
73 | +# |
74 | +# This program is free software; you can redistribute it and/or modify |
75 | +# it under the terms of the GNU General Public License as published by |
76 | +# the Free Software Foundation; either version 2 of the License, or |
77 | +# (at your option) any later version. |
78 | +# |
79 | +# This program is distributed in the hope that it will be useful, |
80 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
81 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
82 | +# GNU General Public License for more details. |
83 | +# |
84 | +# You should have received a copy of the GNU General Public License |
85 | +# along with this program; if not, write to the Free Software |
86 | +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
87 | +# |
88 | +""" |
89 | +SchoolTool Reporlab PDF testing helpers. |
90 | + |
91 | +$Id$ |
92 | + |
93 | +""" |
94 | + |
95 | +import cgi |
96 | +from cStringIO import StringIO |
97 | +from lxml import etree |
98 | + |
99 | +from reportlab import platypus |
100 | + |
101 | + |
102 | +def getStoryXML(story): |
103 | + """Build human readable XML from a story of Reportlab flowables""" |
104 | + parser = Parser(formatters=_xml_formatters, |
105 | + default=format_classname_xml) |
106 | + text = u'<story>\n%s\n</story>' % parser(story) |
107 | + return etree.parse(StringIO(text.encode('UTF-8'))) |
108 | + |
109 | + |
110 | +def printStoryXML(story): |
111 | + print etree.tostring(getStoryXML(story), |
112 | + pretty_print=True) |
113 | + |
114 | +def queryStory(xpath, story): |
115 | + """Perform an XPath query on XML built from the story of |
116 | + Reportlab flowables""" |
117 | + doc = getStoryXML(story) |
118 | + result = [] |
119 | + for node in doc.xpath(xpath): |
120 | + if isinstance(node, basestring): |
121 | + result.append(node) |
122 | + else: |
123 | + result.append(etree.tostring(node, pretty_print=True)) |
124 | + return [s.strip() for s in result] |
125 | + |
126 | + |
127 | +def printQuery(xpath, story): |
128 | + """Print results of XPath query on XML built from the story of |
129 | + Reportlab flowables""" |
130 | + for entry in queryStory(xpath, story): |
131 | + print entry |
132 | + |
133 | + |
134 | +null_formatter = lambda parser, flowable: u'' |
135 | + |
136 | + |
137 | +class Parser(object): |
138 | + def __init__(self, formatters={}, default=null_formatter): |
139 | + self.formatters = formatters.copy() |
140 | + self.default = default |
141 | + |
142 | + def __call__(self, flowable): |
143 | + formatter = self.formatters.get(flowable.__class__, self.default) |
144 | + return formatter(self, flowable) |
145 | + |
146 | + |
147 | +def format_flowable_list(parser, flowables): |
148 | + parsed = [parser(flowable) |
149 | + for flowable in flowables] |
150 | + return '\n'.join([text for text in parsed if text]) |
151 | + |
152 | + |
153 | +def format_str_text(parser, flowable): |
154 | + return unicode(flowable) |
155 | + |
156 | + |
157 | +def format_classname_text(parser, flowable): |
158 | + return unicode(flowable.__class__.__name__) |
159 | + |
160 | + |
161 | +def format_str_xml(parser, flowable): |
162 | + return cgi.escape(unicode(flowable)) |
163 | + |
164 | + |
165 | +def format_classname_xml(parser, flowable): |
166 | + return u'<%s />' % format_classname_text(parser, flowable) |
167 | + |
168 | + |
169 | +def format_container_xml(parser, flowable): |
170 | + tag_name = format_classname_text(parser, flowable) |
171 | + content = parser(flowable._content) |
172 | + return u'<%s>\n%s\n</%s>' % (tag_name, content, tag_name) |
173 | + |
174 | + |
175 | +def format_preformatted_xml(parser, flowable): |
176 | + tag_name = format_classname_text(parser, flowable) |
177 | + return u'<%s bulletText="%s">%s</%s>' % ( |
178 | + tag_name, |
179 | + cgi.escape(flowable.bulletText), |
180 | + cgi.escape(u'\n'.join(flowable.lines)), |
181 | + tag_name) |
182 | + |
183 | + |
184 | +def format_table_xml(parser, flowable): |
185 | + tag_name = format_classname_text(parser, flowable) |
186 | + text = u'<%s>\n' % tag_name |
187 | + for row in flowable._cellvalues: |
188 | + text += '<tr>\n' |
189 | + for cell in row: |
190 | + text += '<td>%s</td>\n' % parser(cell) |
191 | + text += '</tr>\n' |
192 | + text += u'</%s>' % tag_name |
193 | + return text |
194 | + |
195 | + |
196 | +class Format_Attributes_XML(object): |
197 | + def __init__(self, attributes=[], content=''): |
198 | + self.attribute_names = attributes |
199 | + self.content_attribute = content |
200 | + |
201 | + def formatAttr(self, parser, flowable, attr_name): |
202 | + words = [word for word in attr_name.split('_') if word] |
203 | + if words: |
204 | + # first word starts with lower case |
205 | + words[0] = words[0][:1].lower() + words[0][1:] |
206 | + # other words start with upper case |
207 | + words[1:] = [word[:1].upper() + word[1:] for word in words[1:]] |
208 | + pretty_name = ''.join(words) |
209 | + |
210 | + return u'%s="%s"' % ( |
211 | + pretty_name, |
212 | + cgi.escape(str(getattr(flowable, attr_name, None)))) |
213 | + |
214 | + def formatContents(self, parser, flowable): |
215 | + contents = u'' |
216 | + if self.content_attribute: |
217 | + contents = getattr( |
218 | + flowable, self.content_attribute, '') |
219 | + return unicode(cgi.escape(contents)) |
220 | + |
221 | + def __call__(self, parser, flowable): |
222 | + tag_name = format_classname_text(parser, flowable) |
223 | + text = u'<%s' % tag_name |
224 | + for attr_name in self.attribute_names: |
225 | + text += u' %s' % self.formatAttr(parser, flowable, attr_name) |
226 | + |
227 | + contents = self.formatContents(parser, flowable) |
228 | + if contents: |
229 | + text += u'>%s</%s>' % (contents, tag_name) |
230 | + else: |
231 | + text += u' />' |
232 | + |
233 | + return text |
234 | + |
235 | + |
236 | +class Format_Paragraph_XML(Format_Attributes_XML): |
237 | + def __init__(self, attributes=[]): |
238 | + Format_Attributes_XML.__init__(self, attributes=attributes) |
239 | + |
240 | + def formatContents(self, parser, flowable): |
241 | + return unicode(cgi.escape(flowable.getPlainText())) |
242 | + |
243 | + |
244 | +class Format_ParaAndImage_XML(Format_Attributes_XML): |
245 | + |
246 | + def __init__(self): |
247 | + Format_Attributes_XML.__init__(self, ['xpad', 'ypad']) |
248 | + |
249 | + def formatContents(self, parser, flowable): |
250 | + text = parser([flowable.I, flowable.P]) |
251 | + return text and '\n%s\n' % text or '' |
252 | + |
253 | + |
254 | +_xml_formatters = { |
255 | + # system |
256 | + type(None): null_formatter, |
257 | + list: format_flowable_list, |
258 | + |
259 | + # plain text |
260 | + str: format_str_xml, |
261 | + unicode: format_str_xml, |
262 | + |
263 | + # paragraph text |
264 | + platypus.paragraph.Paragraph: Format_Paragraph_XML(), |
265 | + platypus.xpreformatted.XPreformatted: Format_Paragraph_XML( |
266 | + attributes=['bulletText']), |
267 | + platypus.xpreformatted.PythonPreformatted: Format_Paragraph_XML( |
268 | + attributes=['bulletText']), |
269 | + platypus.flowables.Preformatted: format_preformatted_xml, |
270 | + |
271 | + # graphics |
272 | + platypus.flowables.Image: |
273 | + Format_Attributes_XML(['filename', '_width', '_height']), |
274 | + platypus.flowables.HRFlowable: |
275 | + Format_Attributes_XML( |
276 | + ['width', 'lineWidth', 'spaceBefore', 'spaceAfter', |
277 | + 'hAlign', 'vAlign']), |
278 | + |
279 | + # containers |
280 | + platypus.tables.Table: format_table_xml, |
281 | + platypus.tables.LongTable: format_table_xml, |
282 | + platypus.flowables.ParagraphAndImage: Format_ParaAndImage_XML(), |
283 | + #platypus.flowables.ImageAndFlowables |
284 | + #platypus.flowables.PTOContainer, # (Please Turn Over The Page behaviour) |
285 | + |
286 | + # spacing |
287 | + platypus.flowables.KeepInFrame: format_container_xml, |
288 | + platypus.flowables.KeepTogether: format_container_xml, |
289 | + platypus.flowables.PageBreak: format_classname_xml, |
290 | + platypus.flowables.SlowPageBreak: format_classname_xml, |
291 | + platypus.flowables.CondPageBreak: Format_Attributes_XML(['height']), |
292 | + platypus.flowables.Spacer: Format_Attributes_XML( |
293 | + ['width', 'height']), |
294 | + |
295 | + # other |
296 | + platypus.flowables.AnchorFlowable: Format_Attributes_XML(['_name']), |
297 | + #platypus.tableofcontents.TableOfContents, |
298 | + #platypus.tableofcontents.SimpleIndex, |
299 | + |
300 | + # omit from output |
301 | + platypus.flowables.UseUpSpace: null_formatter, |
302 | + platypus.flowables.Flowable: null_formatter, |
303 | + platypus.flowables.TraceInfo: null_formatter, |
304 | + platypus.flowables.Macro: null_formatter, |
305 | + platypus.flowables.CallerMacro: null_formatter, |
306 | + platypus.flowables.FailOnWrap: null_formatter, |
307 | + platypus.flowables.FailOnDraw: null_formatter, |
308 | +} |
309 | + |
310 | |
311 | === added directory 'src/schooltool/testing/tests' |
312 | === removed file 'src/schooltool/testing/tests.py' |
313 | --- src/schooltool/testing/tests.py 2005-10-01 10:25:55 +0000 |
314 | +++ src/schooltool/testing/tests.py 1970-01-01 00:00:00 +0000 |
315 | @@ -1,36 +0,0 @@ |
316 | -# |
317 | -# SchoolTool - common information systems platform for school administration |
318 | -# Copyright (c) 2005 Shuttleworth Foundation |
319 | -# |
320 | -# This program is free software; you can redistribute it and/or modify |
321 | -# it under the terms of the GNU General Public License as published by |
322 | -# the Free Software Foundation; either version 2 of the License, or |
323 | -# (at your option) any later version. |
324 | -# |
325 | -# This program is distributed in the hope that it will be useful, |
326 | -# but WITHOUT ANY WARRANTY; without even the implied warranty of |
327 | -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
328 | -# GNU General Public License for more details. |
329 | -# |
330 | -# You should have received a copy of the GNU General Public License |
331 | -# along with this program; if not, write to the Free Software |
332 | -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
333 | -# |
334 | -""" |
335 | -Testing the package. |
336 | - |
337 | -$Id$ |
338 | -""" |
339 | - |
340 | -import unittest |
341 | -from zope.testing import doctest |
342 | - |
343 | - |
344 | -def test_suite(): |
345 | - return unittest.TestSuite(( |
346 | - doctest.DocFileSuite('README.txt', |
347 | - optionflags=doctest.NORMALIZE_WHITESPACE), |
348 | - )) |
349 | - |
350 | -if __name__ == '__main__': |
351 | - unittest.main(default='test_suite') |
352 | |
353 | === added file 'src/schooltool/testing/tests/__init__.py' |
354 | --- src/schooltool/testing/tests/__init__.py 1970-01-01 00:00:00 +0000 |
355 | +++ src/schooltool/testing/tests/__init__.py 2009-03-30 11:49:24 +0000 |
356 | @@ -0,0 +1,36 @@ |
357 | +# |
358 | +# SchoolTool - common information systems platform for school administration |
359 | +# Copyright (c) 2005 Shuttleworth Foundation |
360 | +# |
361 | +# This program is free software; you can redistribute it and/or modify |
362 | +# it under the terms of the GNU General Public License as published by |
363 | +# the Free Software Foundation; either version 2 of the License, or |
364 | +# (at your option) any later version. |
365 | +# |
366 | +# This program is distributed in the hope that it will be useful, |
367 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
368 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
369 | +# GNU General Public License for more details. |
370 | +# |
371 | +# You should have received a copy of the GNU General Public License |
372 | +# along with this program; if not, write to the Free Software |
373 | +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
374 | +# |
375 | +""" |
376 | +Testing the package. |
377 | + |
378 | +$Id$ |
379 | +""" |
380 | + |
381 | +import unittest |
382 | +from zope.testing import doctest |
383 | + |
384 | + |
385 | +def test_suite(): |
386 | + return unittest.TestSuite(( |
387 | + doctest.DocFileSuite('../README.txt', |
388 | + optionflags=doctest.NORMALIZE_WHITESPACE), |
389 | + )) |
390 | + |
391 | +if __name__ == '__main__': |
392 | + unittest.main(default='test_suite') |
393 | |
394 | === added file 'src/schooltool/testing/tests/test_pdf.py' |
395 | --- src/schooltool/testing/tests/test_pdf.py 1970-01-01 00:00:00 +0000 |
396 | +++ src/schooltool/testing/tests/test_pdf.py 2009-03-30 12:24:08 +0000 |
397 | @@ -0,0 +1,203 @@ |
398 | +# |
399 | +# SchoolTool - common information systems platform for school administration |
400 | +# Copyright (c) 2005 Shuttleworth Foundation |
401 | +# |
402 | +# This program is free software; you can redistribute it and/or modify |
403 | +# it under the terms of the GNU General Public License as published by |
404 | +# the Free Software Foundation; either version 2 of the License, or |
405 | +# (at your option) any later version. |
406 | +# |
407 | +# This program is distributed in the hope that it will be useful, |
408 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
409 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
410 | +# GNU General Public License for more details. |
411 | +# |
412 | +# You should have received a copy of the GNU General Public License |
413 | +# along with this program; if not, write to the Free Software |
414 | +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
415 | +# |
416 | +""" |
417 | +Tests for pdf testing helpers. |
418 | + |
419 | +$Id$ |
420 | +""" |
421 | + |
422 | +import unittest |
423 | +from zope.testing import doctest |
424 | + |
425 | +from reportlab.lib.styles import ParagraphStyle |
426 | +from reportlab.lib import units |
427 | +from reportlab import platypus |
428 | + |
429 | + |
430 | +def buildTestStory(): |
431 | + para_style = ParagraphStyle(name='Test', fontName='Times-Roman') |
432 | + |
433 | + flowables = [] |
434 | + flowables.append('Some\ntext') |
435 | + |
436 | + flowables.append( |
437 | + platypus.flowables.KeepInFrame( |
438 | + units.inch*2, units.inch, content=[ |
439 | + platypus.paragraph.Paragraph('Single line', para_style), |
440 | + u'unicode text'])) |
441 | + flowables.append( |
442 | + platypus.xpreformatted.PythonPreformatted('print "foo"', |
443 | + para_style, bulletText='*')) |
444 | + flowables.append( |
445 | + platypus.flowables.KeepTogether([ |
446 | + platypus.paragraph.Paragraph( |
447 | + 'Multi &\n<b>Line</b>', para_style), |
448 | + platypus.xpreformatted.XPreformatted( |
449 | + 'Text', para_style, bulletText='*'), |
450 | + ], |
451 | + maxHeight=units.inch)) |
452 | + |
453 | + flowables.extend([ |
454 | + platypus.flowables.HRFlowable(), |
455 | + platypus.flowables.Image('logo.png', height=units.inch), |
456 | + platypus.flowables.ParagraphAndImage( |
457 | + platypus.paragraph.Paragraph('Text', para_style), |
458 | + platypus.flowables.Image('file.png'), |
459 | + xpad=units.inch), |
460 | + ]) |
461 | + |
462 | + flowables.extend([ |
463 | + platypus.flowables.PageBreak(), |
464 | + platypus.flowables.SlowPageBreak(), |
465 | + platypus.flowables.CondPageBreak(height=units.inch*2), |
466 | + platypus.flowables.Spacer(units.inch*3, units.inch), |
467 | + ]) |
468 | + |
469 | + flowables.append(platypus.flowables.AnchorFlowable('My anchor')) |
470 | + |
471 | + # also add some uninteresting flowables |
472 | + flowables.append(platypus.flowables.UseUpSpace()) |
473 | + flowables.append(platypus.flowables.Macro('print "foo"')) |
474 | + |
475 | + return flowables |
476 | + |
477 | + |
478 | +def buildTableFlowable(): |
479 | + para_style = ParagraphStyle(name='Test', fontName='Times-Roman') |
480 | + |
481 | + data = [ |
482 | + ['text', |
483 | + platypus.paragraph.Paragraph('Text', para_style)], |
484 | + [['several', 'items in a cell'], |
485 | + platypus.flowables.Image('file.png')], |
486 | + ] |
487 | + return platypus.tables.Table(data) |
488 | + |
489 | + |
490 | +def buildNestedTables(): |
491 | + data = [ |
492 | + ['A table with another table inside!'], |
493 | + [buildTableFlowable()]] |
494 | + return platypus.tables.LongTable(data) |
495 | + |
496 | + |
497 | +def doctest_XML_building(): |
498 | + r"""Tests for getStoryXML and printStoryXML. |
499 | + |
500 | + >>> story = buildTestStory() |
501 | + |
502 | + getStoryXML builds an XML element tree with some some basic flowable |
503 | + parameters. |
504 | + |
505 | + >>> from schooltool.testing.pdf import getStoryXML |
506 | + >>> doc = getStoryXML(story) |
507 | + |
508 | + >>> doc |
509 | + <...ElementTree object ...> |
510 | + |
511 | + printStoryXML builds and prints the XML tree. |
512 | + |
513 | + >>> from schooltool.testing.pdf import printStoryXML |
514 | + >>> printStoryXML(story) |
515 | + <story> |
516 | + Some |
517 | + text |
518 | + <KeepInFrame> |
519 | + <Paragraph>Single line</Paragraph> |
520 | + unicode text |
521 | + </KeepInFrame> |
522 | + <PythonPreformatted bulletText="*">print "foo"</PythonPreformatted> |
523 | + <KeepTogether> |
524 | + <Paragraph>Multi & Line</Paragraph> |
525 | + <XPreformatted bulletText="*">Text</XPreformatted> |
526 | + </KeepTogether> |
527 | + <HRFlowable width="80%" lineWidth="1" |
528 | + spaceBefore="1" spaceAfter="1" |
529 | + hAlign="CENTER" vAlign="BOTTOM"/> |
530 | + <Image filename="logo.png" width="None" height="72.0"/> |
531 | + <ParagraphAndImage xpad="72.0" ypad="3"> |
532 | + <Image filename="file.png" width="None" height="None"/> |
533 | + <Paragraph>Text</Paragraph> |
534 | + </ParagraphAndImage> |
535 | + <PageBreak/> |
536 | + <SlowPageBreak/> |
537 | + <CondPageBreak height="144.0"/> |
538 | + <Spacer width="216.0" height="72.0"/> |
539 | + <AnchorFlowable name="My anchor"/> |
540 | + </story> |
541 | + |
542 | + Test printing of tables. |
543 | + |
544 | + >>> printStoryXML(buildNestedTables()) |
545 | + <story> |
546 | + <LongTable> |
547 | + <tr> |
548 | + <td>A table with another table inside!</td> |
549 | + </tr> |
550 | + <tr> |
551 | + <td><Table> |
552 | + <tr> |
553 | + <td>text</td> |
554 | + <td><Paragraph>Text</Paragraph></td> |
555 | + </tr> |
556 | + <tr> |
557 | + <td>several |
558 | + items in a cell</td> |
559 | + <td><Image filename="file.png" width="None" height="None"/></td> |
560 | + </tr> |
561 | + </Table></td> |
562 | + </tr> |
563 | + </LongTable> |
564 | + </story> |
565 | + |
566 | + """ |
567 | + |
568 | + |
569 | +def doctest_XML_query_helpers(): |
570 | + r"""Tests for queryStory and printQuery. |
571 | + |
572 | + >>> story = buildTestStory() |
573 | + |
574 | + queryStory builds the XML and performs xpath query on it. |
575 | + |
576 | + >>> from schooltool.testing.pdf import queryStory |
577 | + >>> queryStory('//Image', story) |
578 | + ['<Image filename="logo.png" width="None" height="72.0"/>', |
579 | + '<Image filename="file.png" width="None" height="None"/>'] |
580 | + |
581 | + printQuery is a helper which also prints the results: |
582 | + |
583 | + >>> from schooltool.testing.pdf import printQuery |
584 | + >>> printQuery('//Image', story) |
585 | + <Image filename="logo.png" width="None" height="72.0"/> |
586 | + <Image filename="file.png" width="None" height="None"/> |
587 | + |
588 | + """ |
589 | + |
590 | + |
591 | +def test_suite(): |
592 | + optionflags = (doctest.NORMALIZE_WHITESPACE | |
593 | + doctest.ELLIPSIS | |
594 | + doctest.REPORT_NDIFF) |
595 | + return unittest.TestSuite(( |
596 | + doctest.DocTestSuite(optionflags=optionflags), |
597 | + )) |
598 | + |
599 | +if __name__ == '__main__': |
600 | + unittest.main(default='test_suite') |
601 | |
602 | === added file 'src/schooltool/testing/tests/test_readme.py' |
603 | --- src/schooltool/testing/tests/test_readme.py 1970-01-01 00:00:00 +0000 |
604 | +++ src/schooltool/testing/tests/test_readme.py 2009-03-30 12:24:08 +0000 |
605 | @@ -0,0 +1,39 @@ |
606 | +# |
607 | +# SchoolTool - common information systems platform for school administration |
608 | +# Copyright (c) 2005 Shuttleworth Foundation |
609 | +# |
610 | +# This program is free software; you can redistribute it and/or modify |
611 | +# it under the terms of the GNU General Public License as published by |
612 | +# the Free Software Foundation; either version 2 of the License, or |
613 | +# (at your option) any later version. |
614 | +# |
615 | +# This program is distributed in the hope that it will be useful, |
616 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
617 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
618 | +# GNU General Public License for more details. |
619 | +# |
620 | +# You should have received a copy of the GNU General Public License |
621 | +# along with this program; if not, write to the Free Software |
622 | +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
623 | +# |
624 | +""" |
625 | +Test suite for README-style tests. |
626 | + |
627 | +$Id$ |
628 | +""" |
629 | + |
630 | +import unittest |
631 | +from zope.testing import doctest |
632 | + |
633 | + |
634 | +def test_suite(): |
635 | + optionflags = (doctest.NORMALIZE_WHITESPACE | |
636 | + doctest.ELLIPSIS | |
637 | + doctest.REPORT_NDIFF) |
638 | + return unittest.TestSuite(( |
639 | + doctest.DocFileSuite('../README.txt', |
640 | + optionflags=optionflags), |
641 | + )) |
642 | + |
643 | +if __name__ == '__main__': |
644 | + unittest.main(default='test_suite') |
For PDF reports: testing helpers for stories of Reportlab platypus flowables.