~ntt-pf-lab/nova/monkey_patch_notification

« back to all changes in this revision

Viewing changes to vendor/Twisted-10.0.0/twisted/lore/test/test_lore.py

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (c) 2008-2009 Twisted Matrix Laboratories.
 
2
# See LICENSE for details.
 
3
 
 
4
# ++ single anchor added to individual output file
 
5
# ++ two anchors added to individual output file
 
6
# ++ anchors added to individual output files
 
7
# ++ entry added to index
 
8
# ++ index entry pointing to correct file and anchor
 
9
# ++ multiple entries added to index
 
10
# ++ multiple index entries pointing to correct files and anchors
 
11
# __ all of above for files in deep directory structure
 
12
#
 
13
# ++ group index entries by indexed term
 
14
# ++ sort index entries by indexed term
 
15
# __ hierarchical index entries (e.g. language!programming)
 
16
#
 
17
# ++ add parameter for what the index filename should be
 
18
# ++ add (default) ability to NOT index (if index not specified)
 
19
#
 
20
# ++ put actual index filename into INDEX link (if any) in the template
 
21
# __ make index links RELATIVE!
 
22
# __ make index pay attention to the outputdir!
 
23
#
 
24
# __ make index look nice
 
25
#
 
26
# ++ add section numbers to headers in lore output
 
27
# ++ make text of index entry links be chapter numbers
 
28
# ++ make text of index entry links be section numbers
 
29
#
 
30
# __ put all of our test files someplace neat and tidy
 
31
#
 
32
 
 
33
import os, shutil, errno, time
 
34
from StringIO import StringIO
 
35
from xml.dom import minidom as dom
 
36
 
 
37
from twisted.trial import unittest
 
38
from twisted.python.filepath import FilePath
 
39
from twisted.python.versions import Version
 
40
 
 
41
from twisted.lore import tree, process, indexer, numberer, htmlbook, default
 
42
from twisted.lore.default import factory
 
43
from twisted.lore.latex import LatexSpitter
 
44
 
 
45
from twisted.python.util import sibpath
 
46
 
 
47
from twisted.lore.scripts import lore
 
48
 
 
49
from twisted.web import domhelpers
 
50
 
 
51
def sp(originalFileName):
 
52
    return sibpath(__file__, originalFileName)
 
53
 
 
54
options = {"template" : sp("template.tpl"), 'baseurl': '%s', 'ext': '.xhtml' }
 
55
d = options
 
56
 
 
57
 
 
58
class _XMLAssertionMixin:
 
59
    """
 
60
    Test mixin defining a method for comparing serialized XML documents.
 
61
    """
 
62
    def assertXMLEqual(self, first, second):
 
63
        """
 
64
        Verify that two strings represent the same XML document.
 
65
        """
 
66
        self.assertEqual(
 
67
            dom.parseString(first).toxml(),
 
68
            dom.parseString(second).toxml())
 
69
 
 
70
 
 
71
class TestFactory(unittest.TestCase, _XMLAssertionMixin):
 
72
 
 
73
    file = sp('simple.html')
 
74
    linkrel = ""
 
75
 
 
76
    def assertEqualFiles1(self, exp, act):
 
77
        if (exp == act): return True
 
78
        fact = open(act)
 
79
        self.assertEqualsFile(exp, fact.read())
 
80
 
 
81
    def assertEqualFiles(self, exp, act):
 
82
        if (exp == act): return True
 
83
        fact = open(sp(act))
 
84
        self.assertEqualsFile(exp, fact.read())
 
85
 
 
86
    def assertEqualsFile(self, exp, act):
 
87
        expected = open(sp(exp)).read()
 
88
        self.assertEqual(expected, act)
 
89
 
 
90
    def makeTemp(self, *filenames):
 
91
        tmp = self.mktemp()
 
92
        os.mkdir(tmp)
 
93
        for filename in filenames:
 
94
            tmpFile = os.path.join(tmp, filename)
 
95
            shutil.copyfile(sp(filename), tmpFile)
 
96
        return tmp
 
97
 
 
98
########################################
 
99
 
 
100
    def setUp(self):
 
101
        indexer.reset()
 
102
        numberer.reset()
 
103
 
 
104
    def testProcessingFunctionFactory(self):
 
105
        base = FilePath(self.mktemp())
 
106
        base.makedirs()
 
107
 
 
108
        simple = base.child('simple.html')
 
109
        FilePath(__file__).sibling('simple.html').copyTo(simple)
 
110
 
 
111
        htmlGenerator = factory.generate_html(options)
 
112
        htmlGenerator(simple.path, self.linkrel)
 
113
 
 
114
        self.assertXMLEqual(
 
115
            """\
 
116
<?xml version="1.0" ?><!DOCTYPE html  PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN'  'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'><html lang="en" xmlns="http://www.w3.org/1999/xhtml">
 
117
  <head><title>Twisted Documentation: My Test Lore Input</title></head>
 
118
  <body bgcolor="white">
 
119
    <h1 class="title">My Test Lore Input</h1>
 
120
    <div class="content">
 
121
<span/>
 
122
<p>A Body.</p>
 
123
</div>
 
124
    <a href="index.xhtml">Index</a>
 
125
  </body>
 
126
</html>""",
 
127
            simple.sibling('simple.xhtml').getContent())
 
128
 
 
129
 
 
130
    def testProcessingFunctionFactoryWithFilenameGenerator(self):
 
131
        base = FilePath(self.mktemp())
 
132
        base.makedirs()
 
133
 
 
134
        def filenameGenerator(originalFileName, outputExtension):
 
135
            name = os.path.splitext(FilePath(originalFileName).basename())[0]
 
136
            return base.child(name + outputExtension).path
 
137
 
 
138
        htmlGenerator = factory.generate_html(options, filenameGenerator)
 
139
        htmlGenerator(self.file, self.linkrel)
 
140
        self.assertXMLEqual(
 
141
            """\
 
142
<?xml version="1.0" ?><!DOCTYPE html  PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN'  'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'><html lang="en" xmlns="http://www.w3.org/1999/xhtml">
 
143
  <head><title>Twisted Documentation: My Test Lore Input</title></head>
 
144
  <body bgcolor="white">
 
145
    <h1 class="title">My Test Lore Input</h1>
 
146
    <div class="content">
 
147
<span/>
 
148
<p>A Body.</p>
 
149
</div>
 
150
    <a href="index.xhtml">Index</a>
 
151
  </body>
 
152
</html>""",
 
153
            base.child("simple.xhtml").getContent())
 
154
 
 
155
 
 
156
    def test_doFile(self):
 
157
        base = FilePath(self.mktemp())
 
158
        base.makedirs()
 
159
 
 
160
        simple = base.child('simple.html')
 
161
        FilePath(__file__).sibling('simple.html').copyTo(simple)
 
162
 
 
163
        templ = dom.parse(open(d['template']))
 
164
 
 
165
        tree.doFile(simple.path, self.linkrel, d['ext'], d['baseurl'], templ, d)
 
166
        self.assertXMLEqual(
 
167
            """\
 
168
<?xml version="1.0" ?><!DOCTYPE html  PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN'  'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'><html lang="en" xmlns="http://www.w3.org/1999/xhtml">
 
169
  <head><title>Twisted Documentation: My Test Lore Input</title></head>
 
170
  <body bgcolor="white">
 
171
    <h1 class="title">My Test Lore Input</h1>
 
172
    <div class="content">
 
173
<span/>
 
174
<p>A Body.</p>
 
175
</div>
 
176
    <a href="index.xhtml">Index</a>
 
177
  </body>
 
178
</html>""",
 
179
            base.child("simple.xhtml").getContent())
 
180
 
 
181
 
 
182
    def test_doFile_withFilenameGenerator(self):
 
183
        base = FilePath(self.mktemp())
 
184
        base.makedirs()
 
185
 
 
186
        def filenameGenerator(originalFileName, outputExtension):
 
187
            name = os.path.splitext(FilePath(originalFileName).basename())[0]
 
188
            return base.child(name + outputExtension).path
 
189
 
 
190
        templ = dom.parse(open(d['template']))
 
191
        tree.doFile(self.file, self.linkrel, d['ext'], d['baseurl'], templ, d, filenameGenerator)
 
192
 
 
193
        self.assertXMLEqual(
 
194
            """\
 
195
<?xml version="1.0" ?><!DOCTYPE html  PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN'  'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'><html lang="en" xmlns="http://www.w3.org/1999/xhtml">
 
196
  <head><title>Twisted Documentation: My Test Lore Input</title></head>
 
197
  <body bgcolor="white">
 
198
    <h1 class="title">My Test Lore Input</h1>
 
199
    <div class="content">
 
200
<span/>
 
201
<p>A Body.</p>
 
202
</div>
 
203
    <a href="index.xhtml">Index</a>
 
204
  </body>
 
205
</html>""",
 
206
            base.child("simple.xhtml").getContent())
 
207
 
 
208
 
 
209
    def test_munge(self):
 
210
        indexer.setIndexFilename("lore_index_file.html")
 
211
        doc = dom.parse(open(self.file))
 
212
        node = dom.parse(open(d['template']))
 
213
        tree.munge(doc, node, self.linkrel,
 
214
                   os.path.dirname(self.file),
 
215
                   self.file,
 
216
                   d['ext'], d['baseurl'], d)
 
217
 
 
218
        self.assertXMLEqual(
 
219
            """\
 
220
<?xml version="1.0" ?><!DOCTYPE html  PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN'  'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'><html lang="en" xmlns="http://www.w3.org/1999/xhtml">
 
221
  <head><title>Twisted Documentation: My Test Lore Input</title></head>
 
222
  <body bgcolor="white">
 
223
    <h1 class="title">My Test Lore Input</h1>
 
224
    <div class="content">
 
225
<span/>
 
226
<p>A Body.</p>
 
227
</div>
 
228
    <a href="lore_index_file.html">Index</a>
 
229
  </body>
 
230
</html>""",
 
231
            node.toxml())
 
232
 
 
233
 
 
234
    def test_mungeAuthors(self):
 
235
        """
 
236
        If there is a node with a I{class} attribute set to C{"authors"},
 
237
        L{tree.munge} adds anchors as children to it, takeing the necessary
 
238
        information from any I{link} nodes in the I{head} with their I{rel}
 
239
        attribute set to C{"author"}.
 
240
        """
 
241
        document = dom.parseString(
 
242
            """\
 
243
<html>
 
244
  <head>
 
245
    <title>munge authors</title>
 
246
    <link rel="author" title="foo" href="bar"/>
 
247
    <link rel="author" title="baz" href="quux"/>
 
248
    <link rel="author" title="foobar" href="barbaz"/>
 
249
  </head>
 
250
  <body>
 
251
    <h1>munge authors</h1>
 
252
  </body>
 
253
</html>""")
 
254
        template = dom.parseString(
 
255
            """\
 
256
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
 
257
  <head>
 
258
    <title />
 
259
  </head>
 
260
 
 
261
  <body>
 
262
    <div class="body" />
 
263
    <div class="authors" />
 
264
  </body>
 
265
</html>
 
266
""")
 
267
        tree.munge(
 
268
            document, template, self.linkrel, os.path.dirname(self.file),
 
269
            self.file, d['ext'], d['baseurl'], d)
 
270
 
 
271
        self.assertXMLEqual(
 
272
            template.toxml(),
 
273
            """\
 
274
<?xml version="1.0" ?><html lang="en" xmlns="http://www.w3.org/1999/xhtml">
 
275
  <head>
 
276
    <title>munge authors</title>
 
277
  <link href="bar" rel="author" title="foo"/><link href="quux" rel="author" title="baz"/><link href="barbaz" rel="author" title="foobar"/></head>
 
278
 
 
279
  <body>
 
280
    <div class="content">
 
281
    <span/>
 
282
  </div>
 
283
    <div class="authors"><span><a href="bar">foo</a>, <a href="quux">baz</a>, and <a href="barbaz">foobar</a></span></div>
 
284
  </body>
 
285
</html>""")
 
286
 
 
287
 
 
288
    def test_getProcessor(self):
 
289
 
 
290
        base = FilePath(self.mktemp())
 
291
        base.makedirs()
 
292
        input = base.child("simple3.html")
 
293
        FilePath(__file__).sibling("simple3.html").copyTo(input)
 
294
 
 
295
        options = { 'template': sp('template.tpl'), 'ext': '.xhtml', 'baseurl': 'burl',
 
296
                    'filenameMapping': None }
 
297
        p = process.getProcessor(default, "html", options)
 
298
        p(input.path, self.linkrel)
 
299
        self.assertXMLEqual(
 
300
            """\
 
301
<?xml version="1.0" ?><!DOCTYPE html  PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN'  'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'><html lang="en" xmlns="http://www.w3.org/1999/xhtml">
 
302
  <head><title>Twisted Documentation: My Test Lore Input</title></head>
 
303
  <body bgcolor="white">
 
304
    <h1 class="title">My Test Lore Input</h1>
 
305
    <div class="content">
 
306
<span/>
 
307
<p>A Body.</p>
 
308
</div>
 
309
    <a href="index.xhtml">Index</a>
 
310
  </body>
 
311
</html>""",
 
312
            base.child("simple3.xhtml").getContent())
 
313
 
 
314
    def test_outputdirGenerator(self):
 
315
        normp = os.path.normpath; join = os.path.join
 
316
        inputdir  = normp(join("/", 'home', 'joe'))
 
317
        outputdir = normp(join("/", 'away', 'joseph'))
 
318
        actual = process.outputdirGenerator(join("/", 'home', 'joe', "myfile.html"),
 
319
                                            '.xhtml', inputdir, outputdir)
 
320
        expected = normp(join("/", 'away', 'joseph', 'myfile.xhtml'))
 
321
        self.assertEquals(expected, actual)
 
322
 
 
323
    def test_outputdirGeneratorBadInput(self):
 
324
        options = {'outputdir': '/away/joseph/', 'inputdir': '/home/joe/' }
 
325
        self.assertRaises(ValueError, process.outputdirGenerator, '.html', '.xhtml', **options)
 
326
 
 
327
    def test_makeSureDirectoryExists(self):
 
328
        dirname = os.path.join("tmp", 'nonexistentdir')
 
329
        if os.path.exists(dirname):
 
330
            os.rmdir(dirname)
 
331
        self.failIf(os.path.exists(dirname), "Hey: someone already created the dir")
 
332
        filename = os.path.join(dirname, 'newfile')
 
333
        tree.makeSureDirectoryExists(filename)
 
334
        self.failUnless(os.path.exists(dirname), 'should have created dir')
 
335
        os.rmdir(dirname)
 
336
 
 
337
 
 
338
    def test_indexAnchorsAdded(self):
 
339
        indexer.setIndexFilename('theIndexFile.html')
 
340
        # generate the output file
 
341
        templ = dom.parse(open(d['template']))
 
342
        tmp = self.makeTemp('lore_index_test.xhtml')
 
343
 
 
344
        tree.doFile(os.path.join(tmp, 'lore_index_test.xhtml'),
 
345
                    self.linkrel, '.html', d['baseurl'], templ, d)
 
346
 
 
347
        self.assertXMLEqual(
 
348
            """\
 
349
<?xml version="1.0" ?><!DOCTYPE html  PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN'  'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'><html lang="en" xmlns="http://www.w3.org/1999/xhtml">
 
350
  <head><title>Twisted Documentation: The way of the program</title></head>
 
351
  <body bgcolor="white">
 
352
    <h1 class="title">The way of the program</h1>
 
353
    <div class="content">
 
354
 
 
355
<span/>
 
356
 
 
357
<p>The first paragraph.</p>
 
358
 
 
359
 
 
360
<h2>The Python programming language<a name="auto0"/></h2>
 
361
<a name="index01"/>
 
362
<a name="index02"/>
 
363
 
 
364
<p>The second paragraph.</p>
 
365
 
 
366
 
 
367
</div>
 
368
    <a href="theIndexFile.html">Index</a>
 
369
  </body>
 
370
</html>""",
 
371
            FilePath(tmp).child("lore_index_test.html").getContent())
 
372
 
 
373
 
 
374
    def test_indexEntriesAdded(self):
 
375
        indexer.addEntry('lore_index_test.html', 'index02', 'language of programming', '1.3')
 
376
        indexer.addEntry('lore_index_test.html', 'index01', 'programming language', '1.2')
 
377
        indexer.setIndexFilename("lore_index_file.html")
 
378
        indexer.generateIndex()
 
379
        self.assertEqualFiles1("lore_index_file_out.html", "lore_index_file.html")
 
380
 
 
381
    def test_book(self):
 
382
        tmp = self.makeTemp()
 
383
        inputFilename = sp('lore_index_test.xhtml')
 
384
 
 
385
        bookFilename = os.path.join(tmp, 'lore_test_book.book')
 
386
        bf = open(bookFilename, 'w')
 
387
        bf.write('Chapter(r"%s", None)\r\n' % inputFilename)
 
388
        bf.close()
 
389
 
 
390
        book = htmlbook.Book(bookFilename)
 
391
        expected = {'indexFilename': None,
 
392
                    'chapters': [(inputFilename, None)],
 
393
                    }
 
394
        dct = book.__dict__
 
395
        for k in dct:
 
396
            self.assertEquals(dct[k], expected[k])
 
397
 
 
398
    def test_runningLore(self):
 
399
        options = lore.Options()
 
400
        tmp = self.makeTemp('lore_index_test.xhtml')
 
401
 
 
402
        templateFilename = sp('template.tpl')
 
403
        inputFilename = os.path.join(tmp, 'lore_index_test.xhtml')
 
404
        indexFilename = 'theIndexFile'
 
405
 
 
406
        bookFilename = os.path.join(tmp, 'lore_test_book.book')
 
407
        bf = open(bookFilename, 'w')
 
408
        bf.write('Chapter(r"%s", None)\n' % inputFilename)
 
409
        bf.close()
 
410
 
 
411
        options.parseOptions(['--null', '--book=%s' % bookFilename,
 
412
                              '--config', 'template=%s' % templateFilename,
 
413
                              '--index=%s' % indexFilename
 
414
                              ])
 
415
        result = lore.runGivenOptions(options)
 
416
        self.assertEquals(None, result)
 
417
        self.assertEqualFiles1("lore_index_file_unnumbered_out.html", indexFilename + ".html")
 
418
 
 
419
 
 
420
    def test_runningLoreMultipleFiles(self):
 
421
        tmp = self.makeTemp('lore_index_test.xhtml', 'lore_index_test2.xhtml')
 
422
        templateFilename = sp('template.tpl')
 
423
        inputFilename = os.path.join(tmp, 'lore_index_test.xhtml')
 
424
        inputFilename2 = os.path.join(tmp, 'lore_index_test2.xhtml')
 
425
        indexFilename = 'theIndexFile'
 
426
 
 
427
        bookFilename = os.path.join(tmp, 'lore_test_book.book')
 
428
        bf = open(bookFilename, 'w')
 
429
        bf.write('Chapter(r"%s", None)\n' % inputFilename)
 
430
        bf.write('Chapter(r"%s", None)\n' % inputFilename2)
 
431
        bf.close()
 
432
 
 
433
        options = lore.Options()
 
434
        options.parseOptions(['--null', '--book=%s' % bookFilename,
 
435
                              '--config', 'template=%s' % templateFilename,
 
436
                              '--index=%s' % indexFilename
 
437
                              ])
 
438
        result = lore.runGivenOptions(options)
 
439
        self.assertEquals(None, result)
 
440
 
 
441
        self.assertEqual(
 
442
            # XXX This doesn't seem like a very good index file.
 
443
            """\
 
444
aahz: <a href="lore_index_test2.html#index03">link</a><br />
 
445
aahz2: <a href="lore_index_test2.html#index02">link</a><br />
 
446
language of programming: <a href="lore_index_test.html#index02">link</a>, <a href="lore_index_test2.html#index01">link</a><br />
 
447
programming language: <a href="lore_index_test.html#index01">link</a><br />
 
448
""",
 
449
            file(FilePath(indexFilename + ".html").path).read())
 
450
 
 
451
        self.assertXMLEqual(
 
452
            """\
 
453
<?xml version="1.0" ?><!DOCTYPE html  PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN'  'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'><html lang="en" xmlns="http://www.w3.org/1999/xhtml">
 
454
  <head><title>Twisted Documentation: The way of the program</title></head>
 
455
  <body bgcolor="white">
 
456
    <h1 class="title">The way of the program</h1>
 
457
    <div class="content">
 
458
 
 
459
<span/>
 
460
 
 
461
<p>The first paragraph.</p>
 
462
 
 
463
 
 
464
<h2>The Python programming language<a name="auto0"/></h2>
 
465
<a name="index01"/>
 
466
<a name="index02"/>
 
467
 
 
468
<p>The second paragraph.</p>
 
469
 
 
470
 
 
471
</div>
 
472
    <a href="theIndexFile.html">Index</a>
 
473
  </body>
 
474
</html>""",
 
475
            FilePath(tmp).child("lore_index_test.html").getContent())
 
476
 
 
477
        self.assertXMLEqual(
 
478
            """\
 
479
<?xml version="1.0" ?><!DOCTYPE html  PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN'  'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'><html lang="en" xmlns="http://www.w3.org/1999/xhtml">
 
480
  <head><title>Twisted Documentation: The second page to index</title></head>
 
481
  <body bgcolor="white">
 
482
    <h1 class="title">The second page to index</h1>
 
483
    <div class="content">
 
484
 
 
485
<span/>
 
486
 
 
487
<p>The first paragraph of the second page.</p>
 
488
 
 
489
 
 
490
<h2>The Jython programming language<a name="auto0"/></h2>
 
491
<a name="index01"/>
 
492
<a name="index02"/>
 
493
<a name="index03"/>
 
494
 
 
495
<p>The second paragraph of the second page.</p>
 
496
 
 
497
 
 
498
</div>
 
499
    <a href="theIndexFile.html">Index</a>
 
500
  </body>
 
501
</html>""",
 
502
            FilePath(tmp).child("lore_index_test2.html").getContent())
 
503
 
 
504
 
 
505
 
 
506
    def XXXtest_NumberedSections(self):
 
507
        # run two files through lore, with numbering turned on
 
508
        # every h2 should be numbered:
 
509
        # first  file's h2s should be 1.1, 1.2
 
510
        # second file's h2s should be 2.1, 2.2
 
511
        templateFilename = sp('template.tpl')
 
512
        inputFilename = sp('lore_numbering_test.xhtml')
 
513
        inputFilename2 = sp('lore_numbering_test2.xhtml')
 
514
        indexFilename = 'theIndexFile'
 
515
 
 
516
        # you can number without a book:
 
517
        options = lore.Options()
 
518
        options.parseOptions(['--null',
 
519
                              '--index=%s' % indexFilename,
 
520
                              '--config', 'template=%s' % templateFilename,
 
521
                              '--config', 'ext=%s' % ".tns",
 
522
                              '--number',
 
523
                              inputFilename, inputFilename2])
 
524
        result = lore.runGivenOptions(options)
 
525
 
 
526
        self.assertEquals(None, result)
 
527
        #self.assertEqualFiles1("lore_index_file_out_multiple.html", indexFilename + ".tns")
 
528
        #                       VVV change to new, numbered files
 
529
        self.assertEqualFiles("lore_numbering_test_out.html", "lore_numbering_test.tns")
 
530
        self.assertEqualFiles("lore_numbering_test_out2.html", "lore_numbering_test2.tns")
 
531
 
 
532
 
 
533
    def test_setTitle(self):
 
534
        """
 
535
        L{tree.setTitle} inserts the given title into the first I{title}
 
536
        element and the first element with the I{title} class in the given
 
537
        template.
 
538
        """
 
539
        parent = dom.Element('div')
 
540
        firstTitle = dom.Element('title')
 
541
        parent.appendChild(firstTitle)
 
542
        secondTitle = dom.Element('span')
 
543
        secondTitle.setAttribute('class', 'title')
 
544
        parent.appendChild(secondTitle)
 
545
 
 
546
        titleNodes = [dom.Text()]
 
547
        # minidom has issues with cloning documentless-nodes.  See Python issue
 
548
        # 4851.
 
549
        titleNodes[0].ownerDocument = dom.Document()
 
550
        titleNodes[0].data = 'foo bar'
 
551
 
 
552
        tree.setTitle(parent, titleNodes, None)
 
553
        self.assertEqual(firstTitle.toxml(), '<title>foo bar</title>')
 
554
        self.assertEqual(
 
555
            secondTitle.toxml(), '<span class="title">foo bar</span>')
 
556
 
 
557
 
 
558
    def test_setTitleWithChapter(self):
 
559
        """
 
560
        L{tree.setTitle} includes a chapter number if it is passed one.
 
561
        """
 
562
        document = dom.Document()
 
563
 
 
564
        parent = dom.Element('div')
 
565
        parent.ownerDocument = document
 
566
 
 
567
        title = dom.Element('title')
 
568
        parent.appendChild(title)
 
569
 
 
570
        titleNodes = [dom.Text()]
 
571
        titleNodes[0].ownerDocument = document
 
572
        titleNodes[0].data = 'foo bar'
 
573
 
 
574
        # Oh yea.  The numberer has to agree to put the chapter number in, too.
 
575
        numberer.setNumberSections(True)
 
576
 
 
577
        tree.setTitle(parent, titleNodes, '13')
 
578
        self.assertEqual(title.toxml(), '<title>13. foo bar</title>')
 
579
 
 
580
 
 
581
    def test_setIndexLink(self):
 
582
        """
 
583
        Tests to make sure that index links are processed when an index page
 
584
        exists and removed when there is not.
 
585
        """
 
586
        templ = dom.parse(open(d['template']))
 
587
        indexFilename = 'theIndexFile'
 
588
        numLinks = len(domhelpers.findElementsWithAttribute(templ,
 
589
                                                            "class",
 
590
                                                            "index-link"))
 
591
 
 
592
        # if our testing template has no index-link nodes, complain about it
 
593
        self.assertNotEquals(
 
594
            [],
 
595
            domhelpers.findElementsWithAttribute(templ,
 
596
                                                 "class",
 
597
                                                 "index-link"))
 
598
 
 
599
        tree.setIndexLink(templ, indexFilename)
 
600
 
 
601
        self.assertEquals(
 
602
            [],
 
603
            domhelpers.findElementsWithAttribute(templ,
 
604
                                                 "class",
 
605
                                                 "index-link"))
 
606
 
 
607
        indexLinks = domhelpers.findElementsWithAttribute(templ,
 
608
                                                          "href",
 
609
                                                          indexFilename)
 
610
        self.assertTrue(len(indexLinks) >= numLinks)
 
611
 
 
612
        templ = dom.parse(open(d['template']))
 
613
        self.assertNotEquals(
 
614
            [],
 
615
            domhelpers.findElementsWithAttribute(templ,
 
616
                                                 "class",
 
617
                                                 "index-link"))
 
618
        indexFilename = None
 
619
 
 
620
        tree.setIndexLink(templ, indexFilename)
 
621
 
 
622
        self.assertEquals(
 
623
            [],
 
624
            domhelpers.findElementsWithAttribute(templ,
 
625
                                                 "class",
 
626
                                                 "index-link"))
 
627
 
 
628
 
 
629
    def test_addMtime(self):
 
630
        """
 
631
        L{tree.addMtime} inserts a text node giving the last modification time
 
632
        of the specified file wherever it encounters an element with the
 
633
        I{mtime} class.
 
634
        """
 
635
        path = FilePath(self.mktemp())
 
636
        path.setContent('')
 
637
        when = time.ctime(path.getModificationTime())
 
638
 
 
639
        parent = dom.Element('div')
 
640
        mtime = dom.Element('span')
 
641
        mtime.setAttribute('class', 'mtime')
 
642
        parent.appendChild(mtime)
 
643
 
 
644
        tree.addMtime(parent, path.path)
 
645
        self.assertEqual(
 
646
            mtime.toxml(), '<span class="mtime">' + when + '</span>')
 
647
 
 
648
 
 
649
    def test_makeLineNumbers(self):
 
650
        """
 
651
        L{tree._makeLineNumbers} takes an integer and returns a I{p} tag with
 
652
        that number of line numbers in it.
 
653
        """
 
654
        numbers = tree._makeLineNumbers(1)
 
655
        self.assertEqual(numbers.tagName, 'p')
 
656
        self.assertEqual(numbers.getAttribute('class'), 'py-linenumber')
 
657
        self.assertIsInstance(numbers.firstChild, dom.Text)
 
658
        self.assertEqual(numbers.firstChild.nodeValue, '1\n')
 
659
 
 
660
        numbers = tree._makeLineNumbers(10)
 
661
        self.assertEqual(numbers.tagName, 'p')
 
662
        self.assertEqual(numbers.getAttribute('class'), 'py-linenumber')
 
663
        self.assertIsInstance(numbers.firstChild, dom.Text)
 
664
        self.assertEqual(
 
665
            numbers.firstChild.nodeValue,
 
666
            ' 1\n 2\n 3\n 4\n 5\n'
 
667
            ' 6\n 7\n 8\n 9\n10\n')
 
668
 
 
669
 
 
670
    def test_fontifyPythonNode(self):
 
671
        """
 
672
        L{tree.fontifyPythonNode} accepts a text node and replaces it in its
 
673
        parent with a syntax colored and line numbered version of the Python
 
674
        source it contains.
 
675
        """
 
676
        parent = dom.Element('div')
 
677
        source = dom.Text()
 
678
        source.data = 'def foo():\n    pass\n'
 
679
        parent.appendChild(source)
 
680
 
 
681
        tree.fontifyPythonNode(source)
 
682
 
 
683
        expected = """\
 
684
<div><pre class="python"><p class="py-linenumber">1
 
685
2
 
686
</p><span class="py-src-keyword">def</span> <span class="py-src-identifier">foo</span>():
 
687
    <span class="py-src-keyword">pass</span>
 
688
</pre></div>"""
 
689
 
 
690
        self.assertEqual(parent.toxml(), expected)
 
691
 
 
692
 
 
693
    def test_addPyListings(self):
 
694
        """
 
695
        L{tree.addPyListings} accepts a document with nodes with their I{class}
 
696
        attribute set to I{py-listing} and replaces those nodes with Python
 
697
        source listings from the file given by the node's I{href} attribute.
 
698
        """
 
699
        listingPath = FilePath(self.mktemp())
 
700
        listingPath.setContent('def foo():\n    pass\n')
 
701
 
 
702
        parent = dom.Element('div')
 
703
        listing = dom.Element('a')
 
704
        listing.setAttribute('href', listingPath.basename())
 
705
        listing.setAttribute('class', 'py-listing')
 
706
        parent.appendChild(listing)
 
707
 
 
708
        tree.addPyListings(parent, listingPath.dirname())
 
709
 
 
710
        expected = """\
 
711
<div><div class="py-listing"><pre><p class="py-linenumber">1
 
712
2
 
713
</p><span class="py-src-keyword">def</span> <span class="py-src-identifier">foo</span>():
 
714
    <span class="py-src-keyword">pass</span>
 
715
</pre><div class="caption"> - <a href="temp"><span class="filename">temp</span></a></div></div></div>"""
 
716
 
 
717
        self.assertEqual(parent.toxml(), expected)
 
718
 
 
719
 
 
720
    def test_addPyListingsSkipLines(self):
 
721
        """
 
722
        If a node with the I{py-listing} class also has a I{skipLines}
 
723
        attribute, that number of lines from the beginning of the source
 
724
        listing are omitted.
 
725
        """
 
726
        listingPath = FilePath(self.mktemp())
 
727
        listingPath.setContent('def foo():\n    pass\n')
 
728
 
 
729
        parent = dom.Element('div')
 
730
        listing = dom.Element('a')
 
731
        listing.setAttribute('href', listingPath.basename())
 
732
        listing.setAttribute('class', 'py-listing')
 
733
        listing.setAttribute('skipLines', 1)
 
734
        parent.appendChild(listing)
 
735
 
 
736
        tree.addPyListings(parent, listingPath.dirname())
 
737
 
 
738
        expected = """\
 
739
<div><div class="py-listing"><pre><p class="py-linenumber">1
 
740
</p>    <span class="py-src-keyword">pass</span>
 
741
</pre><div class="caption"> - <a href="temp"><span class="filename">temp</span></a></div></div></div>"""
 
742
 
 
743
        self.assertEqual(parent.toxml(), expected)
 
744
 
 
745
 
 
746
    def test_fixAPI(self):
 
747
        """
 
748
        The element passed to L{tree.fixAPI} has all of its children with the
 
749
        I{API} class rewritten to contain links to the API which is referred to
 
750
        by the text they contain.
 
751
        """
 
752
        parent = dom.Element('div')
 
753
        link = dom.Element('span')
 
754
        link.setAttribute('class', 'API')
 
755
        text = dom.Text()
 
756
        text.data = 'foo'
 
757
        link.appendChild(text)
 
758
        parent.appendChild(link)
 
759
 
 
760
        tree.fixAPI(parent, 'http://example.com/%s')
 
761
        self.assertEqual(
 
762
            parent.toxml(),
 
763
            '<div><span class="API">'
 
764
            '<a href="http://example.com/foo" title="foo">foo</a>'
 
765
            '</span></div>')
 
766
 
 
767
 
 
768
    def test_fixAPIBase(self):
 
769
        """
 
770
        If a node with the I{API} class and a value for the I{base} attribute
 
771
        is included in the DOM passed to L{tree.fixAPI}, the link added to that
 
772
        node refers to the API formed by joining the value of the I{base}
 
773
        attribute to the text contents of the node.
 
774
        """
 
775
        parent = dom.Element('div')
 
776
        link = dom.Element('span')
 
777
        link.setAttribute('class', 'API')
 
778
        link.setAttribute('base', 'bar')
 
779
        text = dom.Text()
 
780
        text.data = 'baz'
 
781
        link.appendChild(text)
 
782
        parent.appendChild(link)
 
783
 
 
784
        tree.fixAPI(parent, 'http://example.com/%s')
 
785
 
 
786
        self.assertEqual(
 
787
            parent.toxml(),
 
788
            '<div><span class="API">'
 
789
            '<a href="http://example.com/bar.baz" title="bar.baz">baz</a>'
 
790
            '</span></div>')
 
791
 
 
792
 
 
793
    def test_fixLinks(self):
 
794
        """
 
795
        Links in the nodes of the DOM passed to L{tree.fixLinks} have their
 
796
        extensions rewritten to the given extension.
 
797
        """
 
798
        parent = dom.Element('div')
 
799
        link = dom.Element('a')
 
800
        link.setAttribute('href', 'foo.html')
 
801
        parent.appendChild(link)
 
802
 
 
803
        tree.fixLinks(parent, '.xhtml')
 
804
 
 
805
        self.assertEqual(parent.toxml(), '<div><a href="foo.xhtml"/></div>')
 
806
 
 
807
 
 
808
    def test_setVersion(self):
 
809
        """
 
810
        Nodes of the DOM passed to L{tree.setVersion} which have the I{version}
 
811
        class have the given version added to them a child.
 
812
        """
 
813
        parent = dom.Element('div')
 
814
        version = dom.Element('span')
 
815
        version.setAttribute('class', 'version')
 
816
        parent.appendChild(version)
 
817
 
 
818
        tree.setVersion(parent, '1.2.3')
 
819
 
 
820
        self.assertEqual(
 
821
            parent.toxml(), '<div><span class="version">1.2.3</span></div>')
 
822
 
 
823
 
 
824
    def test_footnotes(self):
 
825
        """
 
826
        L{tree.footnotes} finds all of the nodes with the I{footnote} class in
 
827
        the DOM passed to it and adds a footnotes section to the end of the
 
828
        I{body} element which includes them.  It also inserts links to those
 
829
        footnotes from the original definition location.
 
830
        """
 
831
        parent = dom.Element('div')
 
832
        body = dom.Element('body')
 
833
        footnote = dom.Element('span')
 
834
        footnote.setAttribute('class', 'footnote')
 
835
        text = dom.Text()
 
836
        text.data = 'this is the footnote'
 
837
        footnote.appendChild(text)
 
838
        body.appendChild(footnote)
 
839
        body.appendChild(dom.Element('p'))
 
840
        parent.appendChild(body)
 
841
 
 
842
        tree.footnotes(parent)
 
843
 
 
844
        self.assertEqual(
 
845
            parent.toxml(),
 
846
            '<div><body>'
 
847
            '<a href="#footnote-1" title="this is the footnote">'
 
848
            '<super>1</super>'
 
849
            '</a>'
 
850
            '<p/>'
 
851
            '<h2>Footnotes</h2>'
 
852
            '<ol><li><a name="footnote-1">'
 
853
            '<span class="footnote">this is the footnote</span>'
 
854
            '</a></li></ol>'
 
855
            '</body></div>')
 
856
 
 
857
 
 
858
    def test_generateTableOfContents(self):
 
859
        """
 
860
        L{tree.generateToC} returns an element which contains a table of
 
861
        contents generated from the headers in the document passed to it.
 
862
        """
 
863
        parent = dom.Element('body')
 
864
        header = dom.Element('h2')
 
865
        text = dom.Text()
 
866
        text.data = u'header & special character'
 
867
        header.appendChild(text)
 
868
        parent.appendChild(header)
 
869
        subheader = dom.Element('h3')
 
870
        text = dom.Text()
 
871
        text.data = 'subheader'
 
872
        subheader.appendChild(text)
 
873
        parent.appendChild(subheader)
 
874
 
 
875
        tableOfContents = tree.generateToC(parent)
 
876
        self.assertEqual(
 
877
            tableOfContents.toxml(),
 
878
            '<ol><li><a href="#auto0">header &amp; special character</a></li><ul><li><a href="#auto1">subheader</a></li></ul></ol>')
 
879
 
 
880
        self.assertEqual(
 
881
            header.toxml(),
 
882
            '<h2>header &amp; special character<a name="auto0"/></h2>')
 
883
 
 
884
        self.assertEqual(
 
885
            subheader.toxml(),
 
886
            '<h3>subheader<a name="auto1"/></h3>')
 
887
 
 
888
 
 
889
    def test_putInToC(self):
 
890
        """
 
891
        L{tree.putInToC} replaces all of the children of the first node with
 
892
        the I{toc} class with the given node representing a table of contents.
 
893
        """
 
894
        parent = dom.Element('div')
 
895
        toc = dom.Element('span')
 
896
        toc.setAttribute('class', 'toc')
 
897
        toc.appendChild(dom.Element('foo'))
 
898
        parent.appendChild(toc)
 
899
 
 
900
        tree.putInToC(parent, dom.Element('toc'))
 
901
 
 
902
        self.assertEqual(toc.toxml(), '<span class="toc"><toc/></span>')
 
903
 
 
904
 
 
905
    def test_invalidTableOfContents(self):
 
906
        """
 
907
        If passed a document with I{h3} elements before any I{h2} element,
 
908
        L{tree.generateToC} raises L{ValueError} explaining that this is not a
 
909
        valid document.
 
910
        """
 
911
        parent = dom.Element('body')
 
912
        parent.appendChild(dom.Element('h3'))
 
913
        err = self.assertRaises(ValueError, tree.generateToC, parent)
 
914
        self.assertEqual(
 
915
            str(err), "No H3 element is allowed until after an H2 element")
 
916
 
 
917
 
 
918
    def test_notes(self):
 
919
        """
 
920
        L{tree.notes} inserts some additional markup before the first child of
 
921
        any node with the I{note} class.
 
922
        """
 
923
        parent = dom.Element('div')
 
924
        noteworthy = dom.Element('span')
 
925
        noteworthy.setAttribute('class', 'note')
 
926
        noteworthy.appendChild(dom.Element('foo'))
 
927
        parent.appendChild(noteworthy)
 
928
 
 
929
        tree.notes(parent)
 
930
 
 
931
        self.assertEqual(
 
932
            noteworthy.toxml(),
 
933
            '<span class="note"><strong>Note: </strong><foo/></span>')
 
934
 
 
935
 
 
936
    def test_findNodeJustBefore(self):
 
937
        """
 
938
        L{tree.findNodeJustBefore} returns the previous sibling of the node it
 
939
        is passed.  The list of nodes passed in is ignored.
 
940
        """
 
941
        parent = dom.Element('div')
 
942
        result = dom.Element('foo')
 
943
        target = dom.Element('bar')
 
944
        parent.appendChild(result)
 
945
        parent.appendChild(target)
 
946
 
 
947
        self.assertIdentical(
 
948
            tree.findNodeJustBefore(target, [parent, result]),
 
949
            result)
 
950
 
 
951
        # Also, support other configurations.  This is a really not nice API.
 
952
        newTarget = dom.Element('baz')
 
953
        target.appendChild(newTarget)
 
954
        self.assertIdentical(
 
955
            tree.findNodeJustBefore(newTarget, [parent, result]),
 
956
            result)
 
957
 
 
958
 
 
959
    def test_getSectionNumber(self):
 
960
        """
 
961
        L{tree.getSectionNumber} accepts an I{H2} element and returns its text
 
962
        content.
 
963
        """
 
964
        header = dom.Element('foo')
 
965
        text = dom.Text()
 
966
        text.data = 'foobar'
 
967
        header.appendChild(text)
 
968
        self.assertEqual(tree.getSectionNumber(header), 'foobar')
 
969
 
 
970
 
 
971
    def test_numberDocument(self):
 
972
        """
 
973
        L{tree.numberDocument} inserts section numbers into the text of each
 
974
        header.
 
975
        """
 
976
        parent = dom.Element('foo')
 
977
        section = dom.Element('h2')
 
978
        text = dom.Text()
 
979
        text.data = 'foo'
 
980
        section.appendChild(text)
 
981
        parent.appendChild(section)
 
982
 
 
983
        tree.numberDocument(parent, '7')
 
984
 
 
985
        self.assertEqual(section.toxml(), '<h2>7.1 foo</h2>')
 
986
 
 
987
 
 
988
    def test_parseFileAndReport(self):
 
989
        """
 
990
        L{tree.parseFileAndReport} parses the contents of the filename passed
 
991
        to it and returns the corresponding DOM.
 
992
        """
 
993
        path = FilePath(self.mktemp())
 
994
        path.setContent('<foo bar="baz">hello</foo>\n')
 
995
 
 
996
        document = tree.parseFileAndReport(path.path)
 
997
        self.assertXMLEqual(
 
998
            document.toxml(),
 
999
            '<?xml version="1.0" ?><foo bar="baz">hello</foo>')
 
1000
 
 
1001
 
 
1002
    def test_parseFileAndReportMismatchedTags(self):
 
1003
        """
 
1004
        If the contents of the file passed to L{tree.parseFileAndReport}
 
1005
        contain a mismatched tag, L{process.ProcessingFailure} is raised
 
1006
        indicating the location of the open and close tags which were
 
1007
        mismatched.
 
1008
        """
 
1009
        path = FilePath(self.mktemp())
 
1010
        path.setContent('  <foo>\n\n  </bar>')
 
1011
 
 
1012
        err = self.assertRaises(
 
1013
            process.ProcessingFailure, tree.parseFileAndReport, path.path)
 
1014
        self.assertEqual(
 
1015
            str(err),
 
1016
            "mismatched close tag at line 3, column 4; expected </foo> "
 
1017
            "(from line 1, column 2)")
 
1018
 
 
1019
        # Test a case which requires involves proper close tag handling.
 
1020
        path.setContent('<foo><bar></bar>\n  </baz>')
 
1021
 
 
1022
        err = self.assertRaises(
 
1023
            process.ProcessingFailure, tree.parseFileAndReport, path.path)
 
1024
        self.assertEqual(
 
1025
            str(err),
 
1026
            "mismatched close tag at line 2, column 4; expected </foo> "
 
1027
            "(from line 1, column 0)")
 
1028
 
 
1029
 
 
1030
    def test_parseFileAndReportParseError(self):
 
1031
        """
 
1032
        If the contents of the file passed to L{tree.parseFileAndReport} cannot
 
1033
        be parsed for a reason other than mismatched tags,
 
1034
        L{process.ProcessingFailure} is raised with a string describing the
 
1035
        parse error.
 
1036
        """
 
1037
        path = FilePath(self.mktemp())
 
1038
        path.setContent('\n   foo')
 
1039
 
 
1040
        err = self.assertRaises(
 
1041
            process.ProcessingFailure, tree.parseFileAndReport, path.path)
 
1042
        self.assertEqual(str(err), 'syntax error at line 2, column 3')
 
1043
 
 
1044
 
 
1045
    def test_parseFileAndReportIOError(self):
 
1046
        """
 
1047
        If an L{IOError} is raised while reading from the file specified to
 
1048
        L{tree.parseFileAndReport}, a L{process.ProcessingFailure} is raised
 
1049
        indicating what the error was.  The file should be closed by the
 
1050
        time the exception is raised to the caller.
 
1051
        """
 
1052
        class FakeFile:
 
1053
            _open = True
 
1054
            def read(self, bytes=None):
 
1055
                raise IOError(errno.ENOTCONN, 'socket not connected')
 
1056
 
 
1057
            def close(self):
 
1058
                self._open = False
 
1059
 
 
1060
        theFile = FakeFile()
 
1061
        def fakeOpen(filename):
 
1062
            return theFile
 
1063
 
 
1064
        err = self.assertRaises(
 
1065
            process.ProcessingFailure, tree.parseFileAndReport, "foo", fakeOpen)
 
1066
        self.assertEqual(str(err), "socket not connected, filename was 'foo'")
 
1067
        self.assertFalse(theFile._open)
 
1068
 
 
1069
 
 
1070
 
 
1071
class XMLParsingTests(unittest.TestCase):
 
1072
    """
 
1073
    Tests for various aspects of parsing a Lore XML input document using
 
1074
    L{tree.parseFileAndReport}.
 
1075
    """
 
1076
    def _parseTest(self, xml):
 
1077
        path = FilePath(self.mktemp())
 
1078
        path.setContent(xml)
 
1079
        return tree.parseFileAndReport(path.path)
 
1080
 
 
1081
 
 
1082
    def test_withoutDocType(self):
 
1083
        """
 
1084
        A Lore XML input document may omit a I{DOCTYPE} declaration.  If it
 
1085
        does so, the XHTML1 Strict DTD is used.
 
1086
        """
 
1087
        # Parsing should succeed.
 
1088
        document = self._parseTest("<foo>uses an xhtml entity: &copy;</foo>")
 
1089
        # But even more than that, the &copy; entity should be turned into the
 
1090
        # appropriate unicode codepoint.
 
1091
        self.assertEqual(
 
1092
            domhelpers.gatherTextNodes(document.documentElement),
 
1093
            u"uses an xhtml entity: \N{COPYRIGHT SIGN}")
 
1094
 
 
1095
 
 
1096
    def test_withTransitionalDocType(self):
 
1097
        """
 
1098
        A Lore XML input document may include a I{DOCTYPE} declaration
 
1099
        referring to the XHTML1 Transitional DTD.
 
1100
        """
 
1101
        # Parsing should succeed.
 
1102
        document = self._parseTest("""\
 
1103
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 
1104
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 
1105
<foo>uses an xhtml entity: &copy;</foo>
 
1106
""")
 
1107
        # But even more than that, the &copy; entity should be turned into the
 
1108
        # appropriate unicode codepoint.
 
1109
        self.assertEqual(
 
1110
            domhelpers.gatherTextNodes(document.documentElement),
 
1111
            u"uses an xhtml entity: \N{COPYRIGHT SIGN}")
 
1112
 
 
1113
 
 
1114
    def test_withStrictDocType(self):
 
1115
        """
 
1116
        A Lore XML input document may include a I{DOCTYPE} declaration
 
1117
        referring to the XHTML1 Strict DTD.
 
1118
        """
 
1119
        # Parsing should succeed.
 
1120
        document = self._parseTest("""\
 
1121
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
 
1122
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 
1123
<foo>uses an xhtml entity: &copy;</foo>
 
1124
""")
 
1125
        # But even more than that, the &copy; entity should be turned into the
 
1126
        # appropriate unicode codepoint.
 
1127
        self.assertEqual(
 
1128
            domhelpers.gatherTextNodes(document.documentElement),
 
1129
            u"uses an xhtml entity: \N{COPYRIGHT SIGN}")
 
1130
 
 
1131
 
 
1132
    def test_withDisallowedDocType(self):
 
1133
        """
 
1134
        A Lore XML input document may not include a I{DOCTYPE} declaration
 
1135
        referring to any DTD other than XHTML1 Transitional or XHTML1 Strict.
 
1136
        """
 
1137
        self.assertRaises(
 
1138
            process.ProcessingFailure,
 
1139
            self._parseTest,
 
1140
            """\
 
1141
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
 
1142
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
 
1143
<foo>uses an xhtml entity: &copy;</foo>
 
1144
""")
 
1145
 
 
1146
 
 
1147
 
 
1148
class XMLSerializationTests(unittest.TestCase, _XMLAssertionMixin):
 
1149
    """
 
1150
    Tests for L{tree._writeDocument}.
 
1151
    """
 
1152
    def test_nonASCIIData(self):
 
1153
        """
 
1154
        A document which contains non-ascii characters is serialized to a
 
1155
        file using UTF-8.
 
1156
        """
 
1157
        document = dom.Document()
 
1158
        parent = dom.Element('foo')
 
1159
        text = dom.Text()
 
1160
        text.data = u'\N{SNOWMAN}'
 
1161
        parent.appendChild(text)
 
1162
        document.appendChild(parent)
 
1163
        outFile = self.mktemp()
 
1164
        tree._writeDocument(outFile, document)
 
1165
        self.assertXMLEqual(
 
1166
            FilePath(outFile).getContent(),
 
1167
            u'<foo>\N{SNOWMAN}</foo>'.encode('utf-8'))
 
1168
 
 
1169
 
 
1170
 
 
1171
class LatexSpitterTestCase(unittest.TestCase):
 
1172
    """
 
1173
    Tests for the Latex output plugin.
 
1174
    """
 
1175
    def test_indexedSpan(self):
 
1176
        """
 
1177
        Test processing of a span tag with an index class results in a latex
 
1178
        \\index directive the correct value.
 
1179
        """
 
1180
        doc = dom.parseString('<span class="index" value="name" />').documentElement
 
1181
        out = StringIO()
 
1182
        spitter = LatexSpitter(out.write)
 
1183
        spitter.visitNode(doc)
 
1184
        self.assertEqual(out.getvalue(), u'\\index{name}\n')
 
1185
 
 
1186
 
 
1187
 
 
1188
class ScriptTests(unittest.TestCase):
 
1189
    """
 
1190
    Tests for L{twisted.lore.scripts.lore}, the I{lore} command's
 
1191
    implementation,
 
1192
    """
 
1193
    def test_getProcessor(self):
 
1194
        """
 
1195
        L{lore.getProcessor} loads the specified output plugin from the
 
1196
        specified input plugin.
 
1197
        """
 
1198
        processor = lore.getProcessor("lore", "html", options)
 
1199
        self.assertNotIdentical(processor, None)
 
1200
 
 
1201
 
 
1202
 
 
1203
class DeprecationTests(unittest.TestCase):
 
1204
    """
 
1205
    Tests for deprecated APIs in L{twisted.lore.tree}.
 
1206
    """
 
1207
    def test_comparePosition(self):
 
1208
        """
 
1209
        L{tree.comparePosition} is deprecated.
 
1210
        """
 
1211
        from twisted.web.microdom import parseString
 
1212
        element = parseString('<foo/>').documentElement
 
1213
        self.assertEqual(
 
1214
            self.callDeprecated(
 
1215
                Version('Twisted', 9, 0, 0),
 
1216
                tree.comparePosition, element, element),
 
1217
            0)
 
1218
 
 
1219
 
 
1220
    def test_compareMarkPos(self):
 
1221
        """
 
1222
        L{tree.compareMarkPos} is deprecated.
 
1223
        """
 
1224
        self.assertEqual(
 
1225
            self.callDeprecated(
 
1226
                Version('Twisted', 9, 0, 0),
 
1227
                tree.compareMarkPos, [0, 1], [1, 2]),
 
1228
            -1)