Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3)

Side by Side Diff: third_party/twisted_8_1/twisted/python/test/test_release.py

Issue 12261012: Remove third_party/twisted_8_1 (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 # Copyright (c) 2007-2008 Twisted Matrix Laboratories.
2 # See LICENSE for details.
3
4 """
5 Tests for L{twisted.python.release} and L{twisted.python._release}.
6 """
7
8 import warnings
9 import operator
10 import os, sys, signal
11 from StringIO import StringIO
12 import tarfile
13
14 from datetime import date
15
16 try:
17 import pydoctor.driver
18 # it might not be installed, or it might use syntax not available in
19 # this version of Python.
20 except (ImportError, SyntaxError):
21 pydoctor = None
22
23 from twisted.trial.unittest import TestCase
24
25 from twisted.python.compat import set
26 from twisted.python.procutils import which
27 from twisted.python import release
28 from twisted.python.filepath import FilePath
29 from twisted.python.util import dsu
30 from twisted.python.versions import Version
31 from twisted.python._release import _changeVersionInFile, getNextVersion
32 from twisted.python._release import findTwistedProjects, replaceInFile
33 from twisted.python._release import replaceProjectVersion
34 from twisted.python._release import updateTwistedVersionInformation, Project
35 from twisted.python._release import VERSION_OFFSET, DocBuilder, ManBuilder
36 from twisted.python._release import NoDocumentsFound, filePathDelta
37 from twisted.python._release import CommandFailed, BookBuilder
38 from twisted.python._release import DistributionBuilder, APIBuilder
39
40 try:
41 from twisted.lore.scripts import lore
42 except ImportError:
43 lore = None
44
45
46
47 class ChangeVersionTest(TestCase):
48 """
49 Twisted has the ability to change versions.
50 """
51
52 def makeFile(self, relativePath, content):
53 """
54 Create a file with the given content relative to a temporary directory.
55
56 @param relativePath: The basename of the file to create.
57 @param content: The content that the file will have.
58 @return: The filename.
59 """
60 baseDirectory = FilePath(self.mktemp())
61 directory, filename = os.path.split(relativePath)
62 directory = baseDirectory.preauthChild(directory)
63 directory.makedirs()
64 file = directory.child(filename)
65 directory.child(filename).setContent(content)
66 return file
67
68
69 def test_getNextVersion(self):
70 """
71 When calculating the next version to release when a release is
72 happening in the same year as the last release, the minor version
73 number is incremented.
74 """
75 now = date.today()
76 major = now.year - VERSION_OFFSET
77 version = Version("twisted", major, 9, 0)
78 self.assertEquals(getNextVersion(version, now=now),
79 Version("twisted", major, 10, 0))
80
81
82 def test_getNextVersionAfterYearChange(self):
83 """
84 When calculating the next version to release when a release is
85 happening in a later year, the minor version number is reset to 0.
86 """
87 now = date.today()
88 major = now.year - VERSION_OFFSET
89 version = Version("twisted", major - 1, 9, 0)
90 self.assertEquals(getNextVersion(version, now=now),
91 Version("twisted", major, 0, 0))
92
93
94 def test_changeVersionInFile(self):
95 """
96 _changeVersionInFile replaces the old version information in a file
97 with the given new version information.
98 """
99 # The version numbers are arbitrary, the name is only kind of
100 # arbitrary.
101 packageName = 'foo'
102 oldVersion = Version(packageName, 2, 5, 0)
103 file = self.makeFile('README',
104 "Hello and welcome to %s." % oldVersion.base())
105
106 newVersion = Version(packageName, 7, 6, 0)
107 _changeVersionInFile(oldVersion, newVersion, file.path)
108
109 self.assertEqual(file.getContent(),
110 "Hello and welcome to %s." % newVersion.base())
111
112
113
114 class ProjectTest(TestCase):
115 """
116 There is a first-class representation of a project.
117 """
118
119 def assertProjectsEqual(self, observedProjects, expectedProjects):
120 """
121 Assert that two lists of L{Project}s are equal.
122 """
123 self.assertEqual(len(observedProjects), len(expectedProjects))
124 observedProjects = dsu(observedProjects,
125 key=operator.attrgetter('directory'))
126 expectedProjects = dsu(expectedProjects,
127 key=operator.attrgetter('directory'))
128 for observed, expected in zip(observedProjects, expectedProjects):
129 self.assertEqual(observed.directory, expected.directory)
130
131
132 def makeProject(self, version, baseDirectory=None):
133 """
134 Make a Twisted-style project in the given base directory.
135
136 @param baseDirectory: The directory to create files in
137 (as a L{FilePath).
138 @param version: The version information for the project.
139 @return: L{Project} pointing to the created project.
140 """
141 if baseDirectory is None:
142 baseDirectory = FilePath(self.mktemp())
143 baseDirectory.createDirectory()
144 segments = version.package.split('.')
145 directory = baseDirectory
146 for segment in segments:
147 directory = directory.child(segment)
148 if not directory.exists():
149 directory.createDirectory()
150 directory.child('__init__.py').setContent('')
151 directory.child('topfiles').createDirectory()
152 directory.child('topfiles').child('README').setContent(version.base())
153 replaceProjectVersion(
154 version.package, directory.child('_version.py').path,
155 version)
156 return Project(directory)
157
158
159 def makeProjects(self, *versions):
160 """
161 Create a series of projects underneath a temporary base directory.
162
163 @return: A L{FilePath} for the base directory.
164 """
165 baseDirectory = FilePath(self.mktemp())
166 baseDirectory.createDirectory()
167 for version in versions:
168 self.makeProject(version, baseDirectory)
169 return baseDirectory
170
171
172 def test_getVersion(self):
173 """
174 Project objects know their version.
175 """
176 version = Version('foo', 2, 1, 0)
177 project = self.makeProject(version)
178 self.assertEquals(project.getVersion(), version)
179
180
181 def test_updateVersion(self):
182 """
183 Project objects know how to update the version numbers in those
184 projects.
185 """
186 project = self.makeProject(Version("bar", 2, 1, 0))
187 newVersion = Version("bar", 3, 2, 9)
188 project.updateVersion(newVersion)
189 self.assertEquals(project.getVersion(), newVersion)
190 self.assertEquals(
191 project.directory.child("topfiles").child("README").getContent(),
192 "3.2.9")
193
194
195 def test_repr(self):
196 """
197 The representation of a Project is Project(directory).
198 """
199 foo = Project(FilePath('bar'))
200 self.assertEqual(
201 repr(foo), 'Project(%r)' % (foo.directory))
202
203
204 def test_findTwistedStyleProjects(self):
205 """
206 findTwistedStyleProjects finds all projects underneath a particular
207 directory. A 'project' is defined by the existence of a 'topfiles'
208 directory and is returned as a Project object.
209 """
210 baseDirectory = self.makeProjects(
211 Version('foo', 2, 3, 0), Version('foo.bar', 0, 7, 4))
212 projects = findTwistedProjects(baseDirectory)
213 self.assertProjectsEqual(
214 projects,
215 [Project(baseDirectory.child('foo')),
216 Project(baseDirectory.child('foo').child('bar'))])
217
218
219 def test_updateTwistedVersionInformation(self):
220 """
221 Update Twisted version information in the top-level project and all of
222 the subprojects.
223 """
224 baseDirectory = FilePath(self.mktemp())
225 baseDirectory.createDirectory()
226 now = date.today()
227
228 projectName = 'foo'
229 oldVersion = Version(projectName, 2, 5, 0)
230 newVersion = getNextVersion(oldVersion, now=now)
231
232 project = self.makeProject(oldVersion, baseDirectory)
233
234 updateTwistedVersionInformation(baseDirectory, now=now)
235
236 self.assertEqual(project.getVersion(), newVersion)
237 self.assertEqual(
238 project.directory.child('topfiles').child('README').getContent(),
239 newVersion.base())
240
241
242
243 class UtilityTest(TestCase):
244 """
245 Tests for various utility functions for releasing.
246 """
247
248 def test_chdir(self):
249 """
250 Test that the runChdirSafe is actually safe, i.e., it still
251 changes back to the original directory even if an error is
252 raised.
253 """
254 cwd = os.getcwd()
255 def chAndBreak():
256 os.mkdir('releaseCh')
257 os.chdir('releaseCh')
258 1/0
259 self.assertRaises(ZeroDivisionError,
260 release.runChdirSafe, chAndBreak)
261 self.assertEquals(cwd, os.getcwd())
262
263
264
265 def test_replaceInFile(self):
266 """
267 L{replaceInFile} replaces data in a file based on a dict. A key from
268 the dict that is found in the file is replaced with the corresponding
269 value.
270 """
271 in_ = 'foo\nhey hey $VER\nbar\n'
272 outf = open('release.replace', 'w')
273 outf.write(in_)
274 outf.close()
275
276 expected = in_.replace('$VER', '2.0.0')
277 replaceInFile('release.replace', {'$VER': '2.0.0'})
278 self.assertEquals(open('release.replace').read(), expected)
279
280
281 expected = expected.replace('2.0.0', '3.0.0')
282 replaceInFile('release.replace', {'2.0.0': '3.0.0'})
283 self.assertEquals(open('release.replace').read(), expected)
284
285
286
287 class VersionWritingTest(TestCase):
288 """
289 Tests for L{replaceProjectVersion}.
290 """
291
292 def test_replaceProjectVersion(self):
293 """
294 L{replaceProjectVersion} writes a Python file that defines a
295 C{version} variable that corresponds to the given name and version
296 number.
297 """
298 replaceProjectVersion("twisted.test_project",
299 "test_project", Version("whatever", 0, 82, 7))
300 ns = {'__name___': 'twisted.test_project'}
301 execfile("test_project", ns)
302 self.assertEquals(ns["version"].base(), "0.82.7")
303
304
305 def test_replaceProjectVersionWithPrerelease(self):
306 """
307 L{replaceProjectVersion} will write a Version instantiation that
308 includes a prerelease parameter if necessary.
309 """
310 replaceProjectVersion("twisted.test_project",
311 "test_project", Version("whatever", 0, 82, 7,
312 prerelease=8))
313 ns = {'__name___': 'twisted.test_project'}
314 execfile("test_project", ns)
315 self.assertEquals(ns["version"].base(), "0.82.7pre8")
316
317
318
319 class BuilderTestsMixin(object):
320 """
321 A mixin class which provides various methods for creating sample Lore input
322 and output.
323
324 @cvar template: The lore template that will be used to prepare sample
325 output.
326 @type template: C{str}
327
328 @ivar docCounter: A counter which is incremented every time input is
329 generated and which is included in the documents.
330 @type docCounter: C{int}
331 """
332 template = '''
333 <html>
334 <head><title>Yo:</title></head>
335 <body>
336 <div class="body" />
337 <a href="index.html">Index</a>
338 <span class="version">Version: </span>
339 </body>
340 </html>
341 '''
342
343 def setUp(self):
344 """
345 Initialize the doc counter which ensures documents are unique.
346 """
347 self.docCounter = 0
348
349
350 def getArbitraryOutput(self, version, counter, prefix=""):
351 """
352 Get the correct HTML output for the arbitrary input returned by
353 L{getArbitraryLoreInput} for the given parameters.
354
355 @param version: The version string to include in the output.
356 @type version: C{str}
357 @param counter: A counter to include in the output.
358 @type counter: C{int}
359 """
360 document = ('<?xml version="1.0"?><html><head>'
361 '<title>Yo:Hi! Title: %(count)s</title></head>'
362 '<body><div class="content">Hi! %(count)s'
363 '<div class="API"><a href="foobar" title="foobar">'
364 'foobar</a></div></div><a href="%(prefix)sindex.html">'
365 'Index</a><span class="version">Version: %(version)s'
366 '</span></body></html>')
367 return document % {"count": counter, "prefix": prefix,
368 "version": version}
369
370
371 def getArbitraryLoreInput(self, counter):
372 """
373 Get an arbitrary, unique (for this test case) string of lore input.
374
375 @param counter: A counter to include in the input.
376 @type counter: C{int}
377 """
378 template = (
379 '<html>'
380 '<head><title>Hi! Title: %(count)s</title></head>'
381 '<body>'
382 'Hi! %(count)s'
383 '<div class="API">foobar</div>'
384 '</body>'
385 '</html>')
386 return template % {"count": counter}
387
388
389 def getArbitraryLoreInputAndOutput(self, version, prefix=""):
390 """
391 Get an input document along with expected output for lore run on that
392 output document, assuming an appropriately-specified C{self.template}.
393
394 @param version: A version string to include in the input and output.
395 @type version: C{str}
396 @param prefix: The prefix to include in the link to the index.
397 @type prefix: C{str}
398
399 @return: A two-tuple of input and expected output.
400 @rtype: C{(str, str)}.
401 """
402 self.docCounter += 1
403 return (self.getArbitraryLoreInput(self.docCounter),
404 self.getArbitraryOutput(version, self.docCounter,
405 prefix=prefix))
406
407
408 def getArbitraryManInput(self):
409 """
410 Get an arbitrary man page content.
411 """
412 return """.TH MANHOLE "1" "August 2001" "" ""
413 .SH NAME
414 manhole \- Connect to a Twisted Manhole service
415 .SH SYNOPSIS
416 .B manhole
417 .SH DESCRIPTION
418 manhole is a GTK interface to Twisted Manhole services. You can execute python
419 code as if at an interactive Python console inside a running Twisted process
420 with this."""
421
422
423 def getArbitraryManLoreOutput(self):
424 """
425 Get an arbitrary lore input document which represents man-to-lore
426 output based on the man page returned from L{getArbitraryManInput}
427 """
428 return ("<html><head>\n<title>MANHOLE.1</title>"
429 "</head>\n<body>\n\n<h1>MANHOLE.1</h1>\n\n<h2>NAME</h2>\n\n"
430 "<p>manhole - Connect to a Twisted Manhole service\n</p>\n\n"
431 "<h2>SYNOPSIS</h2>\n\n<p><strong>manhole</strong> </p>\n\n"
432 "<h2>DESCRIPTION</h2>\n\n<p>manhole is a GTK interface to Twisted "
433 "Manhole services. You can execute python\ncode as if at an "
434 "interactive Python console inside a running Twisted process\nwith"
435 " this.</p>\n\n</body>\n</html>\n")
436
437
438 def getArbitraryManHTMLOutput(self, version, prefix=""):
439 """
440 Get an arbitrary lore output document which represents the lore HTML
441 output based on the input document returned from
442 L{getArbitraryManLoreOutput}.
443
444 @param version: A version string to include in the document.
445 @type version: C{str}
446 @param prefix: The prefix to include in the link to the index.
447 @type prefix: C{str}
448 """
449 return ('<?xml version="1.0"?><html><head>'
450 '<title>Yo:MANHOLE.1</title></head><body><div class="content">'
451 '<span></span><h2>NAME<a name="auto0"></a></h2><p>manhole - '
452 'Connect to a Twisted Manhole service\n</p><h2>SYNOPSIS<a '
453 'name="auto1"></a></h2><p><strong>manhole</strong></p><h2>'
454 'DESCRIPTION<a name="auto2"></a></h2><p>manhole is a GTK '
455 'interface to Twisted Manhole services. You can execute '
456 'python\ncode as if at an interactive Python console inside a '
457 'running Twisted process\nwith this.</p></div><a '
458 'href="%sindex.html">Index</a><span class="version">Version: '
459 '%s</span></body></html>' % (prefix, version))
460
461
462
463
464 class DocBuilderTestCase(TestCase, BuilderTestsMixin):
465 """
466 Tests for L{DocBuilder}.
467
468 Note for future maintainers: The exact byte equality assertions throughout
469 this suite may need to be updated due to minor differences in lore. They
470 should not be taken to mean that Lore must maintain the same byte format
471 forever. Feel free to update the tests when Lore changes, but please be
472 careful.
473 """
474
475 def setUp(self):
476 """
477 Set up a few instance variables that will be useful.
478
479 @ivar builder: A plain L{DocBuilder}.
480 @ivar docCounter: An integer to be used as a counter by the
481 C{getArbitrary...} methods.
482 @ivar howtoDir: A L{FilePath} representing a directory to be used for
483 containing Lore documents.
484 @ivar templateFile: A L{FilePath} representing a file with
485 C{self.template} as its content.
486 """
487 BuilderTestsMixin.setUp(self)
488 self.builder = DocBuilder()
489 self.howtoDir = FilePath(self.mktemp())
490 self.howtoDir.createDirectory()
491 self.templateFile = self.howtoDir.child("template.tpl")
492 self.templateFile.setContent(self.template)
493
494
495 def test_build(self):
496 """
497 The L{DocBuilder} runs lore on all .xhtml files within a directory.
498 """
499 version = "1.2.3"
500 input1, output1 = self.getArbitraryLoreInputAndOutput(version)
501 input2, output2 = self.getArbitraryLoreInputAndOutput(version)
502
503 self.howtoDir.child("one.xhtml").setContent(input1)
504 self.howtoDir.child("two.xhtml").setContent(input2)
505
506 self.builder.build(version, self.howtoDir, self.howtoDir,
507 self.templateFile)
508 out1 = self.howtoDir.child('one.html')
509 out2 = self.howtoDir.child('two.html')
510 self.assertEquals(out1.getContent(), output1)
511 self.assertEquals(out2.getContent(), output2)
512
513
514 def test_noDocumentsFound(self):
515 """
516 The C{build} method raises L{NoDocumentsFound} if there are no
517 .xhtml files in the given directory.
518 """
519 self.assertRaises(
520 NoDocumentsFound,
521 self.builder.build, "1.2.3", self.howtoDir, self.howtoDir,
522 self.templateFile)
523
524
525 def test_parentDocumentLinking(self):
526 """
527 The L{DocBuilder} generates correct links from documents to
528 template-generated links like stylesheets and index backreferences.
529 """
530 input = self.getArbitraryLoreInput(0)
531 tutoDir = self.howtoDir.child("tutorial")
532 tutoDir.createDirectory()
533 tutoDir.child("child.xhtml").setContent(input)
534 self.builder.build("1.2.3", self.howtoDir, tutoDir, self.templateFile)
535 outFile = tutoDir.child('child.html')
536 self.assertIn('<a href="../index.html">Index</a>',
537 outFile.getContent())
538
539
540 def test_siblingDirectoryDocumentLinking(self):
541 """
542 It is necessary to generate documentation in a directory foo/bar where
543 stylesheet and indexes are located in foo/baz. Such resources should be
544 appropriately linked to.
545 """
546 input = self.getArbitraryLoreInput(0)
547 resourceDir = self.howtoDir.child("resources")
548 docDir = self.howtoDir.child("docs")
549 docDir.createDirectory()
550 docDir.child("child.xhtml").setContent(input)
551 self.builder.build("1.2.3", resourceDir, docDir, self.templateFile)
552 outFile = docDir.child('child.html')
553 self.assertIn('<a href="../resources/index.html">Index</a>',
554 outFile.getContent())
555
556
557 def test_apiLinking(self):
558 """
559 The L{DocBuilder} generates correct links from documents to API
560 documentation.
561 """
562 version = "1.2.3"
563 input, output = self.getArbitraryLoreInputAndOutput(version)
564 self.howtoDir.child("one.xhtml").setContent(input)
565
566 self.builder.build(version, self.howtoDir, self.howtoDir,
567 self.templateFile, "scheme:apilinks/%s.ext")
568 out = self.howtoDir.child('one.html')
569 self.assertIn(
570 '<a href="scheme:apilinks/foobar.ext" title="foobar">foobar</a>',
571 out.getContent())
572
573
574 def test_deleteInput(self):
575 """
576 L{DocBuilder.build} can be instructed to delete the input files after
577 generating the output based on them.
578 """
579 input1 = self.getArbitraryLoreInput(0)
580 self.howtoDir.child("one.xhtml").setContent(input1)
581 self.builder.build("whatever", self.howtoDir, self.howtoDir,
582 self.templateFile, deleteInput=True)
583 self.assertTrue(self.howtoDir.child('one.html').exists())
584 self.assertFalse(self.howtoDir.child('one.xhtml').exists())
585
586
587 def test_doNotDeleteInput(self):
588 """
589 Input will not be deleted by default.
590 """
591 input1 = self.getArbitraryLoreInput(0)
592 self.howtoDir.child("one.xhtml").setContent(input1)
593 self.builder.build("whatever", self.howtoDir, self.howtoDir,
594 self.templateFile)
595 self.assertTrue(self.howtoDir.child('one.html').exists())
596 self.assertTrue(self.howtoDir.child('one.xhtml').exists())
597
598
599 def test_getLinkrelToSameDirectory(self):
600 """
601 If the doc and resource directories are the same, the linkrel should be
602 an empty string.
603 """
604 linkrel = self.builder.getLinkrel(FilePath("/foo/bar"),
605 FilePath("/foo/bar"))
606 self.assertEquals(linkrel, "")
607
608
609 def test_getLinkrelToParentDirectory(self):
610 """
611 If the doc directory is a child of the resource directory, the linkrel
612 should make use of '..'.
613 """
614 linkrel = self.builder.getLinkrel(FilePath("/foo"),
615 FilePath("/foo/bar"))
616 self.assertEquals(linkrel, "../")
617
618
619 def test_getLinkrelToSibling(self):
620 """
621 If the doc directory is a sibling of the resource directory, the
622 linkrel should make use of '..' and a named segment.
623 """
624 linkrel = self.builder.getLinkrel(FilePath("/foo/howto"),
625 FilePath("/foo/examples"))
626 self.assertEquals(linkrel, "../howto/")
627
628
629 def test_getLinkrelToUncle(self):
630 """
631 If the doc directory is a sibling of the parent of the resource
632 directory, the linkrel should make use of multiple '..'s and a named
633 segment.
634 """
635 linkrel = self.builder.getLinkrel(FilePath("/foo/howto"),
636 FilePath("/foo/examples/quotes"))
637 self.assertEquals(linkrel, "../../howto/")
638
639
640
641 class APIBuilderTestCase(TestCase):
642 """
643 Tests for L{APIBuilder}.
644 """
645 if pydoctor is None or getattr(pydoctor, "version_info", (0,)) < (0, 1):
646 skip = "APIBuilder requires Pydoctor 0.1 or newer"
647
648 def test_build(self):
649 """
650 L{APIBuilder.build} writes an index file which includes the name of the
651 project specified.
652 """
653 stdout = StringIO()
654 self.patch(sys, 'stdout', stdout)
655
656 projectName = "Foobar"
657 packageName = "quux"
658 projectURL = "scheme:project"
659 sourceURL = "scheme:source"
660 docstring = "text in docstring"
661 badDocstring = "should not appear in output"
662
663 inputPath = FilePath(self.mktemp()).child(packageName)
664 inputPath.makedirs()
665 inputPath.child("__init__.py").setContent(
666 "def foo():\n"
667 " '%s'\n"
668 "def _bar():\n"
669 " '%s'" % (docstring, badDocstring))
670
671 outputPath = FilePath(self.mktemp())
672 outputPath.makedirs()
673
674 builder = APIBuilder()
675 builder.build(projectName, projectURL, sourceURL, inputPath, outputPath)
676
677 indexPath = outputPath.child("index.html")
678 self.assertTrue(
679 indexPath.exists(),
680 "API index %r did not exist." % (outputPath.path,))
681 self.assertIn(
682 '<a href="%s">%s</a>' % (projectURL, projectName),
683 indexPath.getContent(),
684 "Project name/location not in file contents.")
685
686 quuxPath = outputPath.child("quux.html")
687 self.assertTrue(
688 quuxPath.exists(),
689 "Package documentation file %r did not exist." % (quuxPath.path,))
690 self.assertIn(
691 docstring, quuxPath.getContent(),
692 "Docstring not in package documentation file.")
693 self.assertIn(
694 '<a href="%s/%s">View Source</a>' % (sourceURL, packageName),
695 quuxPath.getContent())
696 self.assertIn(
697 '<a href="%s/%s">View Source</a>' % (sourceURL, packageName),
698 quuxPath.getContent())
699 self.assertIn(
700 '<a href="%s/%s/__init__.py#L1" class="functionSourceLink">' % (
701 sourceURL, packageName),
702 quuxPath.getContent())
703 self.assertNotIn(badDocstring, quuxPath.getContent())
704
705 self.assertEqual(stdout.getvalue(), '')
706
707
708
709 class ManBuilderTestCase(TestCase, BuilderTestsMixin):
710 """
711 Tests for L{ManBuilder}.
712 """
713
714 def setUp(self):
715 """
716 Set up a few instance variables that will be useful.
717
718 @ivar builder: A plain L{ManBuilder}.
719 @ivar manDir: A L{FilePath} representing a directory to be used for
720 containing man pages.
721 """
722 BuilderTestsMixin.setUp(self)
723 self.builder = ManBuilder()
724 self.manDir = FilePath(self.mktemp())
725 self.manDir.createDirectory()
726
727
728 def test_noDocumentsFound(self):
729 """
730 L{ManBuilder.build} raises L{NoDocumentsFound} if there are no
731 .1 files in the given directory.
732 """
733 self.assertRaises(NoDocumentsFound, self.builder.build, self.manDir)
734
735
736 def test_build(self):
737 """
738 Check that L{ManBuilder.build} find the man page in the directory, and
739 successfully produce a Lore content.
740 """
741 manContent = self.getArbitraryManInput()
742 self.manDir.child('test1.1').setContent(manContent)
743 self.builder.build(self.manDir)
744 output = self.manDir.child('test1-man.xhtml').getContent()
745 expected = self.getArbitraryManLoreOutput()
746 # No-op on *nix, fix for windows
747 expected = expected.replace('\n', os.linesep)
748 self.assertEquals(output, expected)
749
750
751 def test_toHTML(self):
752 """
753 Check that the content output by C{build} is compatible as input of
754 L{DocBuilder.build}.
755 """
756 manContent = self.getArbitraryManInput()
757 self.manDir.child('test1.1').setContent(manContent)
758 self.builder.build(self.manDir)
759
760 templateFile = self.manDir.child("template.tpl")
761 templateFile.setContent(DocBuilderTestCase.template)
762 docBuilder = DocBuilder()
763 docBuilder.build("1.2.3", self.manDir, self.manDir,
764 templateFile)
765 output = self.manDir.child('test1-man.html').getContent()
766 self.assertEquals(output, '<?xml version="1.0"?><html><head>'
767 '<title>Yo:MANHOLE.1</title></head><body><div class="content">'
768 '<span></span><h2>NAME<a name="auto0"></a></h2><p>manhole - '
769 'Connect to a Twisted Manhole service\n</p><h2>SYNOPSIS<a '
770 'name="auto1"></a></h2><p><strong>manhole</strong></p><h2>'
771 'DESCRIPTION<a name="auto2"></a></h2><p>manhole is a GTK '
772 'interface to Twisted Manhole services. You can execute '
773 'python\ncode as if at an interactive Python console inside a '
774 'running Twisted process\nwith this.</p></div><a '
775 'href="index.html">Index</a><span class="version">Version: '
776 '1.2.3</span></body></html>')
777
778
779
780 class BookBuilderTests(TestCase, BuilderTestsMixin):
781 """
782 Tests for L{BookBuilder}.
783 """
784 if not (which("latex") and which("dvips") and which("ps2pdf13")):
785 skip = "Book Builder tests require latex."
786 try:
787 from popen2 import Popen4
788 except ImportError:
789 skip = "Book Builder requires popen2.Popen4."
790 else:
791 del Popen4
792
793 def setUp(self):
794 """
795 Make a directory into which to place temporary files.
796 """
797 self.docCounter = 0
798 self.howtoDir = FilePath(self.mktemp())
799 self.howtoDir.makedirs()
800
801
802 def getArbitraryOutput(self, version, counter, prefix=""):
803 """
804 Create and return a C{str} containing the LaTeX document which is
805 expected as the output for processing the result of the document
806 returned by C{self.getArbitraryLoreInput(counter)}.
807 """
808 path = self.howtoDir.child("%d.xhtml" % (counter,)).path
809 path = path[len(os.getcwd()) + 1:]
810 return (
811 r'\section{Hi! Title: %(count)s\label{%(path)s}}'
812 '\n'
813 r'Hi! %(count)sfoobar') % {'count': counter, 'path': path}
814
815
816 def test_runSuccess(self):
817 """
818 L{BookBuilder.run} executes the command it is passed and returns a
819 string giving the stdout and stderr of the command if it completes
820 successfully.
821 """
822 builder = BookBuilder()
823 self.assertEqual(builder.run("echo hi; echo bye 1>&2"), "hi\nbye\n")
824
825
826 def test_runFailed(self):
827 """
828 L{BookBuilder.run} executes the command it is passed and raises
829 L{CommandFailed} if it completes unsuccessfully.
830 """
831 builder = BookBuilder()
832 exc = self.assertRaises(CommandFailed, builder.run, "echo hi; false")
833 self.assertNotEqual(os.WEXITSTATUS(exc.exitCode), 0)
834 self.assertEqual(exc.output, "hi\n")
835
836
837 def test_runSignaled(self):
838 """
839 L{BookBuilder.run} executes the command it is passed and raises
840 L{CommandFailed} if it exits due to a signal.
841 """
842 builder = BookBuilder()
843 exc = self.assertRaises(
844 # This is only a little bit too tricky.
845 CommandFailed, builder.run, "echo hi; exec kill -9 $$")
846 self.assertTrue(os.WIFSIGNALED(exc.exitCode))
847 self.assertEqual(os.WTERMSIG(exc.exitCode), signal.SIGKILL)
848 self.assertEqual(exc.output, "hi\n")
849
850
851 def test_buildTeX(self):
852 """
853 L{BookBuilder.buildTeX} writes intermediate TeX files for all lore
854 input files in a directory.
855 """
856 version = "3.2.1"
857 input1, output1 = self.getArbitraryLoreInputAndOutput(version)
858 input2, output2 = self.getArbitraryLoreInputAndOutput(version)
859
860 # Filenames are chosen by getArbitraryOutput to match the counter used
861 # by getArbitraryLoreInputAndOutput.
862 self.howtoDir.child("1.xhtml").setContent(input1)
863 self.howtoDir.child("2.xhtml").setContent(input2)
864
865 builder = BookBuilder()
866 builder.buildTeX(self.howtoDir)
867 self.assertEqual(self.howtoDir.child("1.tex").getContent(), output1)
868 self.assertEqual(self.howtoDir.child("2.tex").getContent(), output2)
869
870
871 def test_buildTeXRejectsInvalidDirectory(self):
872 """
873 L{BookBuilder.buildTeX} raises L{ValueError} if passed a directory
874 which does not exist.
875 """
876 builder = BookBuilder()
877 self.assertRaises(
878 ValueError, builder.buildTeX, self.howtoDir.temporarySibling())
879
880
881 def test_buildTeXOnlyBuildsXHTML(self):
882 """
883 L{BookBuilder.buildTeX} ignores files which which don't end with
884 ".xhtml".
885 """
886 # Hopefully ">" is always a parse error from microdom!
887 self.howtoDir.child("not-input.dat").setContent(">")
888 self.test_buildTeX()
889
890
891 def test_stdout(self):
892 """
893 L{BookBuilder.buildTeX} does not write to stdout.
894 """
895 stdout = StringIO()
896 self.patch(sys, 'stdout', stdout)
897
898 # Suppress warnings so that if there are any old-style plugins that
899 # lore queries for don't confuse the assertion below. See #3070.
900 self.patch(warnings, 'warn', lambda *a, **kw: None)
901 self.test_buildTeX()
902 self.assertEqual(stdout.getvalue(), '')
903
904
905 def test_buildPDFRejectsInvalidBookFilename(self):
906 """
907 L{BookBuilder.buildPDF} raises L{ValueError} if the book filename does
908 not end with ".tex".
909 """
910 builder = BookBuilder()
911 self.assertRaises(
912 ValueError,
913 builder.buildPDF,
914 FilePath(self.mktemp()).child("foo"),
915 None,
916 None)
917
918
919 def _setupTeXFiles(self):
920 sections = range(3)
921 self._setupTeXSections(sections)
922 return self._setupTeXBook(sections)
923
924
925 def _setupTeXSections(self, sections):
926 for texSectionNumber in sections:
927 texPath = self.howtoDir.child("%d.tex" % (texSectionNumber,))
928 texPath.setContent(self.getArbitraryOutput(
929 "1.2.3", texSectionNumber))
930
931
932 def _setupTeXBook(self, sections):
933 bookTeX = self.howtoDir.child("book.tex")
934 bookTeX.setContent(
935 r"\documentclass{book}" "\n"
936 r"\begin{document}" "\n" +
937 "\n".join([r"\input{%d.tex}" % (n,) for n in sections]) +
938 r"\end{document}" "\n")
939 return bookTeX
940
941
942 def test_buildPDF(self):
943 """
944 L{BookBuilder.buildPDF} creates a PDF given an index tex file and a
945 directory containing .tex files.
946 """
947 bookPath = self._setupTeXFiles()
948 outputPath = FilePath(self.mktemp())
949
950 builder = BookBuilder()
951 builder.buildPDF(bookPath, self.howtoDir, outputPath)
952
953 self.assertTrue(outputPath.exists())
954
955
956 def test_buildPDFLongPath(self):
957 """
958 L{BookBuilder.buildPDF} succeeds even if the paths it is operating on
959 are very long.
960
961 C{ps2pdf13} seems to have problems when path names are long. This test
962 verifies that even if inputs have long paths, generation still
963 succeeds.
964 """
965 # Make it long.
966 self.howtoDir = self.howtoDir.child("x" * 128).child("x" * 128).child("x " * 128)
967 self.howtoDir.makedirs()
968
969 # This will use the above long path.
970 bookPath = self._setupTeXFiles()
971 outputPath = FilePath(self.mktemp())
972
973 builder = BookBuilder()
974 builder.buildPDF(bookPath, self.howtoDir, outputPath)
975
976 self.assertTrue(outputPath.exists())
977
978
979 def test_buildPDFRunsLaTeXThreeTimes(self):
980 """
981 L{BookBuilder.buildPDF} runs C{latex} three times.
982 """
983 class InspectableBookBuilder(BookBuilder):
984 def __init__(self):
985 BookBuilder.__init__(self)
986 self.commands = []
987
988 def run(self, command):
989 """
990 Record the command and then execute it.
991 """
992 self.commands.append(command)
993 return BookBuilder.run(self, command)
994
995 bookPath = self._setupTeXFiles()
996 outputPath = FilePath(self.mktemp())
997
998 builder = InspectableBookBuilder()
999 builder.buildPDF(bookPath, self.howtoDir, outputPath)
1000
1001 # These string comparisons are very fragile. It would be better to
1002 # have a test which asserted the correctness of the contents of the
1003 # output files. I don't know how one could do that, though. -exarkun
1004 latex1, latex2, latex3, dvips, ps2pdf13 = builder.commands
1005 self.assertEqual(latex1, latex2)
1006 self.assertEqual(latex2, latex3)
1007 self.assertTrue(
1008 latex1.startswith("latex "),
1009 "LaTeX command %r does not start with 'latex '" % (latex1,))
1010 self.assertTrue(
1011 latex1.endswith(" " + bookPath.path),
1012 "LaTeX command %r does not end with the book path (%r)." % (
1013 latex1, bookPath.path))
1014
1015 self.assertTrue(
1016 dvips.startswith("dvips "),
1017 "dvips command %r does not start with 'dvips '" % (dvips,))
1018 self.assertTrue(
1019 ps2pdf13.startswith("ps2pdf13 "),
1020 "ps2pdf13 command %r does not start with 'ps2pdf13 '" % (
1021 ps2pdf13,))
1022
1023
1024 def test_noSideEffects(self):
1025 """
1026 The working directory is the same before and after a call to
1027 L{BookBuilder.buildPDF}. Also the contents of the directory containing
1028 the input book are the same before and after the call.
1029 """
1030 startDir = os.getcwd()
1031 bookTeX = self._setupTeXFiles()
1032 startTeXSiblings = bookTeX.parent().children()
1033 startHowtoChildren = self.howtoDir.children()
1034
1035 builder = BookBuilder()
1036 builder.buildPDF(bookTeX, self.howtoDir, FilePath(self.mktemp()))
1037
1038 self.assertEqual(startDir, os.getcwd())
1039 self.assertEqual(startTeXSiblings, bookTeX.parent().children())
1040 self.assertEqual(startHowtoChildren, self.howtoDir.children())
1041
1042
1043 def test_failedCommandProvidesOutput(self):
1044 """
1045 If a subprocess fails, L{BookBuilder.buildPDF} raises L{CommandFailed}
1046 with the subprocess's output and leaves the temporary directory as a
1047 sibling of the book path.
1048 """
1049 bookTeX = FilePath(self.mktemp() + ".tex")
1050 builder = BookBuilder()
1051 inputState = bookTeX.parent().children()
1052 exc = self.assertRaises(
1053 CommandFailed,
1054 builder.buildPDF,
1055 bookTeX, self.howtoDir, FilePath(self.mktemp()))
1056 self.assertTrue(exc.output)
1057 newOutputState = set(bookTeX.parent().children()) - set(inputState)
1058 self.assertEqual(len(newOutputState), 1)
1059 workPath = newOutputState.pop()
1060 self.assertTrue(
1061 workPath.isdir(),
1062 "Expected work path %r was not a directory." % (workPath.path,))
1063
1064
1065 def test_build(self):
1066 """
1067 L{BookBuilder.build} generates a pdf book file from some lore input
1068 files.
1069 """
1070 sections = range(1, 4)
1071 for sectionNumber in sections:
1072 self.howtoDir.child("%d.xhtml" % (sectionNumber,)).setContent(
1073 self.getArbitraryLoreInput(sectionNumber))
1074 bookTeX = self._setupTeXBook(sections)
1075 bookPDF = FilePath(self.mktemp())
1076
1077 builder = BookBuilder()
1078 builder.build(self.howtoDir, [self.howtoDir], bookTeX, bookPDF)
1079
1080 self.assertTrue(bookPDF.exists())
1081
1082
1083 def test_buildRemovesTemporaryLaTeXFiles(self):
1084 """
1085 L{BookBuilder.build} removes the intermediate LaTeX files it creates.
1086 """
1087 sections = range(1, 4)
1088 for sectionNumber in sections:
1089 self.howtoDir.child("%d.xhtml" % (sectionNumber,)).setContent(
1090 self.getArbitraryLoreInput(sectionNumber))
1091 bookTeX = self._setupTeXBook(sections)
1092 bookPDF = FilePath(self.mktemp())
1093
1094 builder = BookBuilder()
1095 builder.build(self.howtoDir, [self.howtoDir], bookTeX, bookPDF)
1096
1097 self.assertEqual(
1098 set(self.howtoDir.listdir()),
1099 set([bookTeX.basename()] + ["%d.xhtml" % (n,) for n in sections]))
1100
1101
1102
1103 class FilePathDeltaTest(TestCase):
1104 """
1105 Tests for L{filePathDelta}.
1106 """
1107
1108 def test_filePathDeltaSubdir(self):
1109 """
1110 L{filePathDelta} can create a simple relative path to a child path.
1111 """
1112 self.assertEquals(filePathDelta(FilePath("/foo/bar"),
1113 FilePath("/foo/bar/baz")),
1114 ["baz"])
1115
1116
1117 def test_filePathDeltaSiblingDir(self):
1118 """
1119 L{filePathDelta} can traverse upwards to create relative paths to
1120 siblings.
1121 """
1122 self.assertEquals(filePathDelta(FilePath("/foo/bar"),
1123 FilePath("/foo/baz")),
1124 ["..", "baz"])
1125
1126
1127 def test_filePathNoCommonElements(self):
1128 """
1129 L{filePathDelta} can create relative paths to totally unrelated paths
1130 for maximum portability.
1131 """
1132 self.assertEquals(filePathDelta(FilePath("/foo/bar"),
1133 FilePath("/baz/quux")),
1134 ["..", "..", "baz", "quux"])
1135
1136
1137
1138 class DistributionBuilderTests(BuilderTestsMixin, TestCase):
1139 """
1140 Tests for L{DistributionBuilder}.
1141 """
1142
1143 def setUp(self):
1144 BuilderTestsMixin.setUp(self)
1145
1146 self.rootDir = FilePath(self.mktemp())
1147 self.rootDir.createDirectory()
1148
1149 outputDir = FilePath(self.mktemp())
1150 outputDir.createDirectory()
1151 self.builder = DistributionBuilder(self.rootDir, outputDir)
1152
1153
1154 def createStructure(self, root, dirDict):
1155 """
1156 Create a set of directories and files given a dict defining their
1157 structure.
1158
1159 @param root: The directory in which to create the structure.
1160 @type root: L{FilePath}
1161
1162 @param dirDict: The dict defining the structure. Keys should be strings
1163 naming files, values should be strings describing file contents OR
1164 dicts describing subdirectories. For example: C{{"foofile":
1165 "foocontents", "bardir": {"barfile": "barcontents"}}}
1166 @type dirDict: C{dict}
1167 """
1168 for x in dirDict:
1169 child = root.child(x)
1170 if isinstance(dirDict[x], dict):
1171 child.createDirectory()
1172 self.createStructure(child, dirDict[x])
1173 else:
1174 child.setContent(dirDict[x])
1175
1176
1177 def assertExtractedStructure(self, outputFile, dirDict):
1178 """
1179 Assert that a tarfile content is equivalent to one described by a dict.
1180
1181 @param outputFile: The tar file built by L{DistributionBuilder}.
1182 @type outputFile: L{FilePath}.
1183 @param dirDict: The dict that should describe the contents of the
1184 directory. It should be the same structure as the C{dirDict}
1185 parameter to L{createStructure}.
1186 @type dirDict: C{dict}
1187 """
1188 tarFile = tarfile.TarFile.open(outputFile.path, "r:bz2")
1189 extracted = FilePath(self.mktemp())
1190 extracted.createDirectory()
1191 for info in tarFile:
1192 tarFile.extract(info, path=extracted.path)
1193 self.assertStructure(extracted.children()[0], dirDict)
1194
1195
1196 def assertStructure(self, root, dirDict):
1197 """
1198 Assert that a directory is equivalent to one described by a dict.
1199
1200 @param root: The filesystem directory to compare.
1201 @type root: L{FilePath}
1202 @param dirDict: The dict that should describe the contents of the
1203 directory. It should be the same structure as the C{dirDict}
1204 parameter to L{createStructure}.
1205 @type dirDict: C{dict}
1206 """
1207 children = [x.basename() for x in root.children()]
1208 for x in dirDict:
1209 child = root.child(x)
1210 if isinstance(dirDict[x], dict):
1211 self.assertTrue(child.isdir(), "%s is not a dir!"
1212 % (child.path,))
1213 self.assertStructure(child, dirDict[x])
1214 else:
1215 a = child.getContent()
1216 self.assertEquals(a, dirDict[x], child.path)
1217 children.remove(x)
1218 if children:
1219 self.fail("There were extra children in %s: %s"
1220 % (root.path, children))
1221
1222
1223 def test_twistedDistribution(self):
1224 """
1225 The Twisted tarball contains everything in the source checkout, with
1226 built documentation.
1227 """
1228 loreInput, loreOutput = self.getArbitraryLoreInputAndOutput("10.0.0")
1229 manInput1 = self.getArbitraryManInput()
1230 manOutput1 = self.getArbitraryManHTMLOutput("10.0.0", "../howto/")
1231 manInput2 = self.getArbitraryManInput()
1232 manOutput2 = self.getArbitraryManHTMLOutput("10.0.0", "../howto/")
1233 coreIndexInput, coreIndexOutput = self.getArbitraryLoreInputAndOutput(
1234 "10.0.0", prefix="howto/")
1235
1236 structure = {
1237 "README": "Twisted",
1238 "unrelated": "x",
1239 "LICENSE": "copyright!",
1240 "setup.py": "import toplevel",
1241 "bin": {"web": {"websetroot": "SET ROOT"},
1242 "twistd": "TWISTD"},
1243 "twisted":
1244 {"web":
1245 {"__init__.py": "import WEB",
1246 "topfiles": {"setup.py": "import WEBINSTALL",
1247 "README": "WEB!"}},
1248 "words": {"__init__.py": "import WORDS"},
1249 "plugins": {"twisted_web.py": "import WEBPLUG",
1250 "twisted_words.py": "import WORDPLUG"}},
1251 "doc": {"web": {"howto": {"index.xhtml": loreInput},
1252 "man": {"websetroot.1": manInput2}},
1253 "core": {"howto": {"template.tpl": self.template},
1254 "man": {"twistd.1": manInput1},
1255 "index.xhtml": coreIndexInput}}}
1256
1257 outStructure = {
1258 "README": "Twisted",
1259 "unrelated": "x",
1260 "LICENSE": "copyright!",
1261 "setup.py": "import toplevel",
1262 "bin": {"web": {"websetroot": "SET ROOT"},
1263 "twistd": "TWISTD"},
1264 "twisted":
1265 {"web": {"__init__.py": "import WEB",
1266 "topfiles": {"setup.py": "import WEBINSTALL",
1267 "README": "WEB!"}},
1268 "words": {"__init__.py": "import WORDS"},
1269 "plugins": {"twisted_web.py": "import WEBPLUG",
1270 "twisted_words.py": "import WORDPLUG"}},
1271 "doc": {"web": {"howto": {"index.html": loreOutput},
1272 "man": {"websetroot.1": manInput2,
1273 "websetroot-man.html": manOutput2}},
1274 "core": {"howto": {"template.tpl": self.template},
1275 "man": {"twistd.1": manInput1,
1276 "twistd-man.html": manOutput1},
1277 "index.html": coreIndexOutput}}}
1278
1279 self.createStructure(self.rootDir, structure)
1280
1281 outputFile = self.builder.buildTwisted("10.0.0")
1282
1283 self.assertExtractedStructure(outputFile, outStructure)
1284
1285 def test_twistedDistributionExcludesWeb2AndVFS(self):
1286 """
1287 The main Twisted distribution does not include web2 or vfs.
1288 """
1289 loreInput, loreOutput = self.getArbitraryLoreInputAndOutput("10.0.0")
1290 coreIndexInput, coreIndexOutput = self.getArbitraryLoreInputAndOutput(
1291 "10.0.0", prefix="howto/")
1292
1293 structure = {
1294 "README": "Twisted",
1295 "unrelated": "x",
1296 "LICENSE": "copyright!",
1297 "setup.py": "import toplevel",
1298 "bin": {"web2": {"websetroot": "SET ROOT"},
1299 "vfs": {"vfsitup": "hee hee"},
1300 "twistd": "TWISTD"},
1301 "twisted":
1302 {"web2":
1303 {"__init__.py": "import WEB",
1304 "topfiles": {"setup.py": "import WEBINSTALL",
1305 "README": "WEB!"}},
1306 "vfs":
1307 {"__init__.py": "import VFS",
1308 "blah blah": "blah blah"},
1309 "words": {"__init__.py": "import WORDS"},
1310 "plugins": {"twisted_web.py": "import WEBPLUG",
1311 "twisted_words.py": "import WORDPLUG",
1312 "twisted_web2.py": "import WEB2",
1313 "twisted_vfs.py": "import VFS"}},
1314 "doc": {"web2": {"excluded!": "yay"},
1315 "vfs": {"unrelated": "whatever"},
1316 "core": {"howto": {"template.tpl": self.template},
1317 "index.xhtml": coreIndexInput}}}
1318
1319 outStructure = {
1320 "README": "Twisted",
1321 "unrelated": "x",
1322 "LICENSE": "copyright!",
1323 "setup.py": "import toplevel",
1324 "bin": {"twistd": "TWISTD"},
1325 "twisted":
1326 {"words": {"__init__.py": "import WORDS"},
1327 "plugins": {"twisted_web.py": "import WEBPLUG",
1328 "twisted_words.py": "import WORDPLUG"}},
1329 "doc": {"core": {"howto": {"template.tpl": self.template},
1330 "index.html": coreIndexOutput}}}
1331 self.createStructure(self.rootDir, structure)
1332
1333 outputFile = self.builder.buildTwisted("10.0.0")
1334
1335 self.assertExtractedStructure(outputFile, outStructure)
1336
1337
1338 def test_subProjectLayout(self):
1339 """
1340 The subproject tarball includes files like so:
1341
1342 1. twisted/<subproject>/topfiles defines the files that will be in the
1343 top level in the tarball, except LICENSE, which comes from the real
1344 top-level directory.
1345 2. twisted/<subproject> is included, but without the topfiles entry
1346 in that directory. No other twisted subpackages are included.
1347 3. twisted/plugins/twisted_<subproject>.py is included, but nothing
1348 else in plugins is.
1349 """
1350 structure = {
1351 "README": "HI!@",
1352 "unrelated": "x",
1353 "LICENSE": "copyright!",
1354 "setup.py": "import toplevel",
1355 "bin": {"web": {"websetroot": "SET ROOT"},
1356 "words": {"im": "#!im"}},
1357 "twisted":
1358 {"web":
1359 {"__init__.py": "import WEB",
1360 "topfiles": {"setup.py": "import WEBINSTALL",
1361 "README": "WEB!"}},
1362 "words": {"__init__.py": "import WORDS"},
1363 "plugins": {"twisted_web.py": "import WEBPLUG",
1364 "twisted_words.py": "import WORDPLUG"}}}
1365
1366 outStructure = {
1367 "README": "WEB!",
1368 "LICENSE": "copyright!",
1369 "setup.py": "import WEBINSTALL",
1370 "bin": {"websetroot": "SET ROOT"},
1371 "twisted": {"web": {"__init__.py": "import WEB"},
1372 "plugins": {"twisted_web.py": "import WEBPLUG"}}}
1373
1374 self.createStructure(self.rootDir, structure)
1375
1376 outputFile = self.builder.buildSubProject("web", "0.3.0")
1377
1378 self.assertExtractedStructure(outputFile, outStructure)
1379
1380
1381 def test_minimalSubProjectLayout(self):
1382 """
1383 buildSubProject should work with minimal subprojects.
1384 """
1385 structure = {
1386 "LICENSE": "copyright!",
1387 "bin": {},
1388 "twisted":
1389 {"web": {"__init__.py": "import WEB",
1390 "topfiles": {"setup.py": "import WEBINSTALL"}},
1391 "plugins": {}}}
1392
1393 outStructure = {
1394 "setup.py": "import WEBINSTALL",
1395 "LICENSE": "copyright!",
1396 "twisted": {"web": {"__init__.py": "import WEB"}}}
1397
1398 self.createStructure(self.rootDir, structure)
1399
1400 outputFile = self.builder.buildSubProject("web", "0.3.0")
1401
1402 self.assertExtractedStructure(outputFile, outStructure)
1403
1404
1405 def test_subProjectDocBuilding(self):
1406 """
1407 When building a subproject release, documentation should be built with
1408 lore.
1409 """
1410 loreInput, loreOutput = self.getArbitraryLoreInputAndOutput("0.3.0")
1411 manInput = self.getArbitraryManInput()
1412 manOutput = self.getArbitraryManHTMLOutput("0.3.0", "../howto/")
1413 structure = {
1414 "LICENSE": "copyright!",
1415 "twisted": {"web": {"__init__.py": "import WEB",
1416 "topfiles": {"setup.py": "import WEBINST"}}},
1417 "doc": {"web": {"howto": {"index.xhtml": loreInput},
1418 "man": {"twistd.1": manInput}},
1419 "core": {"howto": {"template.tpl": self.template}}
1420 }
1421 }
1422
1423 outStructure = {
1424 "LICENSE": "copyright!",
1425 "setup.py": "import WEBINST",
1426 "twisted": {"web": {"__init__.py": "import WEB"}},
1427 "doc": {"howto": {"index.html": loreOutput},
1428 "man": {"twistd.1": manInput,
1429 "twistd-man.html": manOutput}}}
1430
1431 self.createStructure(self.rootDir, structure)
1432
1433 outputFile = self.builder.buildSubProject("web", "0.3.0")
1434
1435 self.assertExtractedStructure(outputFile, outStructure)
1436
1437
1438 def test_coreProjectLayout(self):
1439 """
1440 The core tarball looks a lot like a subproject tarball, except it
1441 doesn't include:
1442
1443 - Python packages from other subprojects
1444 - plugins from other subprojects
1445 - scripts from other subprojects
1446 """
1447 indexInput, indexOutput = self.getArbitraryLoreInputAndOutput(
1448 "8.0.0", prefix="howto/")
1449 howtoInput, howtoOutput = self.getArbitraryLoreInputAndOutput("8.0.0")
1450 specInput, specOutput = self.getArbitraryLoreInputAndOutput(
1451 "8.0.0", prefix="../howto/")
1452 upgradeInput, upgradeOutput = self.getArbitraryLoreInputAndOutput(
1453 "8.0.0", prefix="../howto/")
1454 tutorialInput, tutorialOutput = self.getArbitraryLoreInputAndOutput(
1455 "8.0.0", prefix="../")
1456
1457 structure = {
1458 "LICENSE": "copyright!",
1459 "twisted": {"__init__.py": "twisted",
1460 "python": {"__init__.py": "python",
1461 "roots.py": "roots!"},
1462 "conch": {"__init__.py": "conch",
1463 "unrelated.py": "import conch"},
1464 "plugin.py": "plugin",
1465 "plugins": {"twisted_web.py": "webplug",
1466 "twisted_whatever.py": "include!",
1467 "cred.py": "include!"},
1468 "topfiles": {"setup.py": "import CORE",
1469 "README": "core readme"}},
1470 "doc": {"core": {"howto": {"template.tpl": self.template,
1471 "index.xhtml": howtoInput,
1472 "tutorial":
1473 {"index.xhtml": tutorialInput}},
1474 "specifications": {"index.xhtml": specInput},
1475 "upgrades": {"index.xhtml": upgradeInput},
1476 "examples": {"foo.py": "foo.py"},
1477 "index.xhtml": indexInput},
1478 "web": {"howto": {"index.xhtml": "webindex"}}},
1479 "bin": {"twistd": "TWISTD",
1480 "web": {"websetroot": "websetroot"}}
1481 }
1482
1483 outStructure = {
1484 "LICENSE": "copyright!",
1485 "setup.py": "import CORE",
1486 "README": "core readme",
1487 "twisted": {"__init__.py": "twisted",
1488 "python": {"__init__.py": "python",
1489 "roots.py": "roots!"},
1490 "plugin.py": "plugin",
1491 "plugins": {"twisted_whatever.py": "include!",
1492 "cred.py": "include!"}},
1493 "doc": {"howto": {"template.tpl": self.template,
1494 "index.html": howtoOutput,
1495 "tutorial": {"index.html": tutorialOutput}},
1496 "specifications": {"index.html": specOutput},
1497 "upgrades": {"index.html": upgradeOutput},
1498 "examples": {"foo.py": "foo.py"},
1499 "index.html": indexOutput},
1500 "bin": {"twistd": "TWISTD"},
1501 }
1502
1503 self.createStructure(self.rootDir, structure)
1504
1505 outputFile = self.builder.buildCore("8.0.0")
1506
1507 self.assertExtractedStructure(outputFile, outStructure)
1508
1509
1510
1511 if lore is None:
1512 skipMessage = "Lore is not present."
1513 BookBuilderTests.skip = skipMessage
1514 DocBuilderTestCase.skip = skipMessage
1515 ManBuilderTestCase.skip = skipMessage
1516 DistributionBuilderTests.skip = skipMessage
OLDNEW
« no previous file with comments | « third_party/twisted_8_1/twisted/python/test/test_dist.py ('k') | third_party/twisted_8_1/twisted/python/test/test_util.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698