Commits

Mike Steder committed 6f84f25

Initial checkin of txProject

  • Participants

Comments (0)

Files changed (23)

File bin/dirprinter

+#!/usr/bin/env python
+#-*- mode: python -*-
+"""dirprinter
+
+Pretty prints directories like this:
+
+Example:
+$ dirprinter dirname
+
+  dirname/
+  |-- printer.py
+  `-- testdir/
+      |-- subdir/
+      |-- |-- test3.txt
+      |-- `-- test4.txt
+      |-- test1.txt
+      `-- test2.txt
+
+"""
+
+import sys
+
+from txproject import printer
+from txproject import scripts
+
+parser = scripts.getDirprinterOptionsParser(__doc__)
+options, args = parser.parse_args()
+if len(args) != 1:
+    parser.error("requires a single directory name as an argument")
+path = args[0]
+printer.printDirectory(path)

File bin/txproject

+#!/usr/bin/env python
+#-*- mode: python -*-
+"""
+txproject
+
+A script for creating Twisted Project skeletons.
+
+usage:
+  txproject [options] <ProjectName>
+
+options:
+  --help
+
+<ProjectName> should follow the syntax rules for valid python module names.
+
+"""
+
+import sys
+
+from txproject import factory
+from txproject import printer
+from txproject import scripts
+
+parser = scripts.getTxprojectOptionsParser(__doc__)
+options, args = parser.parse_args()
+if len(args) == 1:
+    f = factory.NewProject(args[0], "default", ".")
+    f.makeDirectories()
+    f.makeFiles()
+    printer.printDirectory(f.projectName())
+else:
+    parser.error("provide a project name")
+
+= txProject =
+== About ==
+
+txProject is a set of utilities for quickly creating getting simple Twisted Python based
+projects off the ground with all the convenient boilerplate already in place.
+
+By providing a working skeleton with tests, plugin files, and basic configuration
+txProject makes it easier for a twisted newbie to see how things fit together.  They
+get an idea how they might organize their code and they have something that is working
+immediately that they can begin to explore.
+
+txProject is driven by basic templates and these templates can easily be customized
+to provide more or fewer features to fit the developers needs.
+
+== Installation ==
+
+=== Standard installation with Distutils ===
+
+$ tar -zxvf txProject-1.0.tar.gz
+$ cd txProject
+$ python setup.py build
+$ sudo python setup.py install
+
+== Example Usage ==
+
+txProject provides a txproject command.
+
+=== Creating a simple Twisted Web application ===
+
+$ txproject hello
+
+This command creates the following:
+
+Hello/
+|-- bin/
+|-- hello/
+|   |-- test/
+|   |   `-- test_web.py
+|   `-- web.py
+`-- twisted/
+        plugins/
+            hello_plugin.py
+                    

File txproject/__init__.py

+#__init__.py

File txproject/factory.py

+#-*- test-case-name: txproject.test_factory -*-
+"""
+defines project factory
+
+The project factory is responsible for loading a project template and
+creating a project directory with the specified template and project name.
+
+"""
+
+import os
+
+from txproject import template
+
+
+class NewProject(object):
+    def __init__(self, projectName, templateName, root=".", templateRoot=template.DEFAULT_TEMPLATE_DIR):
+        self.name = projectName.lower()
+        self.templateName = templateName
+        self._template = None
+        self.root = root
+        self.templateRoot = templateRoot
+        
+    def getTemplate(self):
+        """Loads template *self.templateName*
+
+        """
+        if self._template is None:
+            r = template.TemplateRegistry()
+            self._template = r.templates[self.templateName]
+        return self._template
+
+    def projectName(self):
+        return self.name.capitalize()
+
+    def directories(self):
+        dirs = []
+        for dir in self.getTemplate().directories():
+            d = dir.replace("<project>", self.name)
+            dirs.append(d)
+        return dirs
+
+    def files(self):
+        files = []
+        for f in self.getTemplate().files():
+            for path, realPath in f.iteritems():
+                nf = path.replace("<project>", self.name)
+                files.append(nf)
+        return files
+
+    def makeDirectories(self):
+        for d in self.directories():
+            path = os.path.join(self.root,
+                             self.projectName(),
+                             d)
+            print "path:", path
+            os.makedirs(path)
+            
+    def makeFiles(self):
+        for templateFile in self.getTemplate().files():
+            for f, realPath in templateFile.iteritems():
+                f = f.replace("<project>", self.name)
+                realPath = realPath.replace("<project>", self.name)
+                path = os.path.join(self.root,
+                                    self.projectName(),
+                                    f)
+                print "path:", path
+
+                contents = open(os.path.join(self.templateRoot, realPath), "r").read()
+                contents = contents.replace(self.templateName.capitalize(), self.projectName())
+                contents = contents.replace(self.templateName, self.name)
+                o = open(path, "w")
+                o.write(contents)
+                o.close()
+            

File txproject/printer.py

+"""
+Pretty prints directories like this:
+
+    dirprint/
+    |-- printer.py
+    `-- testdir/
+        |-- subdir/
+        |-- |-- test3.txt
+        |-- `-- test4.txt
+        |-- test1.txt
+        `-- test2.txt
+
+"""
+
+import os
+
+lastCounter = {}
+buffer = []
+
+
+def printLine(path, lastFlags):
+    #print "flags:", lastFlags
+    tailChar = ""
+    if os.path.isdir(path):
+        tailChar = "/"
+    trail = []
+    for index, last in enumerate(lastFlags):
+        icon = "|-- "
+        if last:
+            icon = "`-- "
+        elif (index + 1) < len(lastFlags):
+            icon = "|   "
+
+        if index not in lastCounter:
+            lastCounter[index] = {"value":last,
+                                  "icon":icon,
+                                  "count":0}
+        else:
+            if lastCounter[index]["value"] == last and last == True:
+                lastCounter[index]["count"] += 1
+                lastCounter[index]["icon"] = icon
+            else:
+                lastCounter[index]["value"] = last
+                lastCounter[index]["count"] = 0
+                lastCounter[index]["icon"] = icon
+
+        #print "lastCounter[%s][\"count\"]: %s (%s)"%(index,
+        #                                        lastCounter[index]["count"],
+        # len(lastFlags))
+        if lastCounter[index]["count"] > 0 and (index + 1) < len(lastFlags):
+            icon = "    "
+        trail.append(icon)
+    name = os.path.basename(path)
+    line = "".join(trail) + name + tailChar
+    print line
+    buffer.append(line)
+
+
+def walk(top, flags):
+    flags.append(False)
+    contents = [os.path.join(top, name) for name in os.listdir(top)]
+    contents.sort()
+    for index, path in enumerate(contents):
+        last = ((index+1)==len(contents))
+        flags[-1] = last
+        printLine(path, flags)
+        if os.path.isdir(path):
+            flags[-1] = last
+            walk(path, flags)
+            del flags[-1]
+
+
+def printDirectory(path):
+    """
+    takes a single string argument: *path*
+    
+    returns a pretty formatted string representing the directory at *path*
+
+    """
+    global buffer, lastCounter
+    buffer = []
+    lastCounter = {}
+    path = os.path.abspath(path)
+    printLine(path, [])
+    walk(path, [])
+    return "\n".join(buffer)
+

File txproject/scripts.py

+"""scripts.py
+
+Defines option parsers for all the commandline scripts in txProject
+
+"""
+
+
+from optparse import OptionParser
+
+
+def getDirprinterOptionsParser(usage):
+    parser = OptionParser(usage)
+    return parser
+
+
+def getTxprojectOptionsParser(usage):
+    parser = OptionParser(usage)
+    return parser

File txproject/template.py

+#-*- test-case-name: txproject.test_template -*-
+"""
+Reads a template into memory
+"""
+
+import os
+import simplejson
+
+
+DEFAULT_TEMPLATE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "templates")
+
+
+class Template(object):
+    def __init__(self, name, template_data):
+        self.name = name
+        self.template_data = template_data
+
+    def directories(self):
+        """Returns a list of directories to be
+        created as part of this template
+
+        """
+        return self.template_data["directories"]
+
+    def files(self):
+        """Returns a list of files to be created
+        as part of this template
+
+        """
+        return self.template_data["files"]
+
+
+class TemplateRegistry(object):
+    """Contains lookup table of all available
+    templates.  Can be asked to provide
+    an entire template configuration and data
+
+    """
+    def __init__(self, template_dir=DEFAULT_TEMPLATE_DIR):
+        self.template_dir = template_dir
+        self.templates = {}
+        if self.template_dir is not None:
+            tocFile = open(os.path.join(self.template_dir, "index.json"), "r")
+            toc = simplejson.load(
+                tocFile
+            )
+            for templateName in toc:
+                self.templates[templateName] = Template(templateName, toc[templateName])

File txproject/templates/Default/default/__init__.py

+# __init__.py 

File txproject/templates/Default/default/server.py

+#-*- test-case-name: default.test.test_server -*-
+"""
+Defines the Default Server
+
+"""
+from twisted.application import internet, service
+from twisted.internet import defer, reactor
+from twisted.python import log
+from twisted.web import server
+
+from default.web import root
+
+
+class DefaultServer(service.MultiService):
+    def __init__(self, port):
+        service.MultiService.__init__(self)
+        webServerFactory = server.Site(root.Root())
+        webServer = internet.TCPServer(port, webServerFactory)
+        webServer.setName("Default")
+        webServer.setServiceParent(self)
+
+    def _cbStarted(self, result):
+        service.MultiService.startService(self)        
+        return result
+
+    def _ebError(self, failure):
+        log.err("failure starting service:", failure)
+        return failure
+
+    def startupHook(self, deferred):
+        """Include additional startup steps here
+
+        """
+        deferred.callback(True)
+
+    def startService(self):
+        d = defer.Deferred()
+        d.addCallback(self._cbStarted)
+        d.addErrback(self._ebError)
+        reactor.callWhenRunning(self.startupHook, d)
+        return d
+
+    def stopService(self):
+        print "Shutting down service:"
+        return service.MultiService.stopService(self)

File txproject/templates/Default/default/settings.py

+"""Settings for Default
+
+"""
+
+from twisted.python import filepath
+
+default_root = filepath.FilePath(__file__).parent().parent()
+    
+hostname = "localhost"
+port = 7777
+

File txproject/templates/Default/default/static/root.html

+<html>
+  <head>
+    <title>Default Test Page</title>
+  </head>
+  <body>
+    <h1>Default Test Page</h1>
+  </body>
+</html>

File txproject/templates/Default/default/test/__init__.py

+# __init__.py

File txproject/templates/Default/default/test/test_server.py

+from twisted.python import failure
+from twisted.trial import unittest
+
+from default import server
+
+class TestDefaultServer(unittest.TestCase):
+    def setUp(self):
+        portNumber = 6666
+        self.server = server.DefaultServer(portNumber)
+        
+    def test_serverName(self):
+        self.assertEqual(None, self.server.name
+                         )
+
+class TestDefaultServerStart(unittest.TestCase):
+    def setUp(self):
+        portNumber = 6666
+        self.server = server.DefaultServer(portNumber)
+        def _fakeServiceCallback(result):
+            return result
+        self.server._cbStarted = _fakeServiceCallback
+
+    def tearDown(self):
+        d = self.server.stopService()
+        return d
+
+    def _startSuccess(self, result):
+        print "test_server._startSuccess:", result
+        self.assertEqual(True, result, "Expect server start to return true result")
+        return result
+
+    def _startShouldNotFail(self, failure):
+        print failure
+        self.fail("start should succeed!")
+        return failure
+
+    def _startShouldNotSucceed(self, result):
+        self.fail("Start shouldn't succeed!")
+
+    def _startShouldFail(self, failure):
+        self.assertTrue(failure is not None)
+
+    def test_startService(self):
+        d = self.server.startService()
+        d.addCallbacks(callback=self._startSuccess,
+                       errback=self._startShouldNotFail)
+        return d
+
+    def test_startServiceFails(self):
+        def fakeStart(d):
+            d.errback(failure.Failure(Exception("Some exception occured while starting the service")))
+        self.server.startupHook = fakeStart
+        d = self.server.startService()
+        d.addCallbacks(callback=self._startShouldNotSucceed,
+                       errback=self._startShouldFail)
+        return d
+        
+

File txproject/templates/Default/default/test/test_web.py

+from twisted.trial import unittest
+from twisted.web import server
+from twisted.web.test import test_web
+
+from default.web import root
+
+class TestRootResource(unittest.TestCase):
+    def setUp(self):
+        self.r = root.Root()
+
+    def test_create(self):
+        r = root.Root()
+        self.assertTrue(r is not None)
+
+    def test_renderGet(self):
+        """should get the login page"""
+        result = self.r.render_GET(test_web.DummyRequest(['']))
+        self.assertTrue(result == server.NOT_DONE_YET)
+        return self.r.d
+        

File txproject/templates/Default/default/web/__init__.py

+# __init__.py

File txproject/templates/Default/default/web/root.py

+#-*- test-case-name: default.test.test_web -*-
+import os
+
+from twisted.python import log
+from twisted.internet import defer, reactor
+from twisted.web import http, resource, server, static
+
+from default import settings
+
+
+class Root(resource.Resource):
+    def __init__(self):
+        resource.Resource.__init__(self)
+        self.putChild("static", static.File("default/static"))
+
+    def _failed(self, reason):
+        print "_failed:", reason
+        log.err(reason)
+        return http.Response(
+            code=500,
+            headers=None,
+            stream=reason.getErrorMessage()
+        )
+
+    def _got(self, result, request):
+        print "_got:", result, request
+        request.setResponseCode(http.OK)
+        request.write(result)
+        request.finish()
+
+    def getStaticFile(self, deferred):
+        print "getStaticFile", deferred
+        filepath = os.path.join(settings.default_root.path, "default/static/root.html")
+        print "opening file:", filepath
+        template = open(filepath, "r")
+        print "opened file"
+        contents = template.read()
+        print "calling back:", contents
+        deferred.callback(contents)
+        print "called back"
+
+    def render_GET(self, request):
+        self.d = defer.Deferred()
+        self.d.addCallback(self._got, request)
+        self.d.addErrback(self._failed)
+        reactor.callLater(0.1, self.getStaticFile, self.d)
+        print "returning server.NOT_DONE_YET"
+        return server.NOT_DONE_YET
+
+    def getChild(self, path, request):
+        if path == "":
+            return self
+        return resource.Resource.getChild(self, path, request)

File txproject/templates/Default/twisted/plugins/default_plugin.py

+from zope import interface
+
+from twisted import plugin
+from twisted.application import service
+from twisted.python import usage
+
+from default import server, settings
+
+
+class Options(usage.Options):
+    optParameters = (
+        ('port', 'p', None, 'Port on which to listen.'),
+    )
+
+
+class DefaultServiceMaker(object):
+    interface.implements(plugin.IPlugin, service.IServiceMaker)
+    description = "Default"
+    options = Options
+    tapname = 'default'
+
+    def makeService(self, options):
+        """
+        Return an instance of default.server.DefaultServer
+        """
+        port = settings.port
+
+        if options['port'] is not None:
+            port = int(options['port'])
+
+        return server.DefaultServer(port)
+
+
+serviceMaker = DefaultServiceMaker()

File txproject/templates/index.json

+{"test":{
+      "directories":[
+        "<project>/test",
+        "twisted/plugins"
+      ],
+      "files":[
+        {"<project>/web.py":"web.py"},
+        {"<project>/test/test_web.py":"test_web.py"},
+        {"twisted/plugins/<project>_plugin.py":"web_plugin.py"}
+      ]
+  },
+ "default":{
+        "directories":[
+            "<project>/static",
+            "<project>/test",
+            "<project>/web",
+            "twisted/plugins"
+        ],
+        "files":[
+            {"<project>/__init__.py":"Default/default/__init__.py"},
+            {"<project>/server.py":"Default/default/server.py"},
+            {"<project>/settings.py":"Default/default/settings.py"},
+            {"<project>/static/root.html":"Default/default/static/root.html"},
+            {"<project>/web/__init__.py":"Default/default/web/__init__.py"},
+            {"<project>/web/root.py":"Default/default/web/root.py"},
+            {"<project>/test/__init__.py":"Default/default/test/__init__.py"},
+            {"<project>/test/test_server.py":"Default/default/test/test_server.py"},
+            {"<project>/test/test_web.py":"Default/default/test/test_web.py"},
+            {"twisted/plugins/<project>_plugin.py":"Default/twisted/plugins/default_plugin.py"}
+        ]
+ }
+}
+

File txproject/test_dirprint.py

+import os
+import shutil
+import unittest
+
+from printer import printDirectory
+
+ROOT = os.path.dirname(os.path.abspath(__file__))
+
+def writeTestFileToPath(path):
+    testFileContents = "I am a test file."
+    f = open(path, "w")
+    f.write(testFileContents)
+    f.close()
+
+
+class TestEmptyDirectory(unittest.TestCase):
+    def setUp(self):
+        os.makedirs(os.path.join(ROOT,"testdir"))
+
+    def tearDown(self):
+        shutil.rmtree(os.path.join(ROOT,"testdir"))
+
+    def test_dirprint(self):
+        s = printDirectory(os.path.join(ROOT,"testdir"))
+        expected = """testdir/"""
+        print "expected:"
+        print expected
+        print "got:"
+        print s
+        self.assertEquals(expected, s)
+
+
+class TestOneFileDirectory(unittest.TestCase):
+    def setUp(self):
+        os.makedirs(os.path.join(ROOT,"testdir"))
+        testFiles = ["testdir/test1.txt",
+                     ]
+        for filePath in testFiles:
+            writeTestFileToPath(os.path.join(ROOT, filePath))
+
+    def tearDown(self):
+        shutil.rmtree(os.path.join(ROOT,"testdir"))
+
+    def test_dirprint(self):
+        s = printDirectory(os.path.join(ROOT,"testdir"))
+        expected = """testdir/
+`-- test1.txt"""
+        print "expected:"
+        print expected
+        print "got:"
+        print s
+        self.assertEquals(expected, s)
+
+
+class TestTwoFileDirectory(unittest.TestCase):
+    def setUp(self):
+        os.makedirs(os.path.join(ROOT,"testdir"))
+        testFiles = ["testdir/test1.txt",
+                     "testdir/test2.txt",
+                     ]
+        for filePath in testFiles:
+            writeTestFileToPath(os.path.join(ROOT, filePath))
+
+    def tearDown(self):
+        shutil.rmtree(os.path.join(ROOT,"testdir"))
+
+    def test_dirprint(self):
+        s = printDirectory(os.path.join(ROOT,"testdir"))
+        expected = """testdir/
+|-- test1.txt
+`-- test2.txt"""
+        print "expected:"
+        print expected
+        print "got:"
+        print s
+        self.assertEquals(expected, s)
+
+
+class TestNestedDirectories(unittest.TestCase):
+    def setUp(self):
+        os.makedirs(os.path.join(ROOT,"testdir","subdir"))
+        testFiles = ["testdir/subdir/test1.txt",
+                     "testdir/subdir/test2.txt",
+                     ]
+        for filePath in testFiles:
+            writeTestFileToPath(os.path.join(ROOT, filePath))
+
+    def tearDown(self):
+        shutil.rmtree(os.path.join(ROOT,"testdir"))
+
+    def test_dirprint(self):
+        s = printDirectory(os.path.join(ROOT,"testdir"))
+        expected = """testdir/
+`-- subdir/
+    |-- test1.txt
+    `-- test2.txt"""
+        print "expected:"
+        print expected
+        print "got:"
+        print s
+        self.assertEquals(expected, s)
+
+
+class TestNestedDirectoriesWithMultipleTopLevelFiles(unittest.TestCase):
+    def setUp(self):
+        os.makedirs(os.path.join(ROOT,"testdir","subdir"))
+        testFiles = ["testdir/test1.txt",
+                     "testdir/test2.txt",
+                     "testdir/subdir/test3.txt",
+                     "testdir/subdir/test4.txt",
+                     ]
+        for filePath in testFiles:
+            writeTestFileToPath(os.path.join(ROOT, filePath))
+
+    def tearDown(self):
+        shutil.rmtree(os.path.join(ROOT,"testdir"))
+
+    def test_dirprint(self):
+        s = printDirectory(os.path.join(ROOT,"testdir"))
+        expected = """testdir/
+|-- subdir/
+|   |-- test3.txt
+|   `-- test4.txt
+|-- test1.txt
+`-- test2.txt"""
+        print "expected:"
+        print expected
+        print "got:"
+        print s
+        self.assertEquals(expected, s)
+
+
+class TestMultipleNestedDirectoriesMultipleFiles(unittest.TestCase):
+    def setUp(self):
+        os.makedirs(os.path.join(ROOT,"testdir","subdir1"))
+        os.makedirs(os.path.join(ROOT,"testdir","subdir2"))
+        testFiles = ["testdir/test1.txt",
+                     "testdir/test2.txt",
+                     "testdir/subdir1/test3.txt",
+                     "testdir/subdir1/test4.txt",
+                     "testdir/subdir2/test5.txt",
+                     "testdir/subdir2/test6.txt",
+                     ]
+        for filePath in testFiles:
+            writeTestFileToPath(os.path.join(ROOT, filePath))
+
+    def tearDown(self):
+        shutil.rmtree(os.path.join(ROOT,"testdir"))
+
+    def test_dirprint(self):
+        s = printDirectory(os.path.join(ROOT,"testdir"))
+        expected = """testdir/
+|-- subdir1/
+|   |-- test3.txt
+|   `-- test4.txt
+|-- subdir2/
+|   |-- test5.txt
+|   `-- test6.txt
+|-- test1.txt
+`-- test2.txt"""
+        print s
+        self.assertEquals(expected, s)
+
+
+class TestTwistedSinglePlugin(unittest.TestCase):
+    def setUp(self):
+        os.makedirs(os.path.join(ROOT,"testdir","test","web"))
+        os.makedirs(os.path.join(ROOT,"testdir","twisted","plugins"))
+        testFiles = ["testdir/test1.txt",
+                     "testdir/test2.txt",
+                     "testdir/test/__init__.py",
+                     "testdir/test/web/__init__.py",
+                     "testdir/test/web/root.py",
+                     "testdir/twisted/plugins/testplugin.py",
+                     ]
+        for filePath in testFiles:
+            writeTestFileToPath(os.path.join(ROOT, filePath))
+
+    def tearDown(self):
+        shutil.rmtree(os.path.join(ROOT,"testdir"))
+
+    def test_dirprint(self):
+        s = printDirectory(os.path.join(ROOT,"testdir"))
+        expected = """testdir/
+|-- test/
+|   |-- __init__.py
+|   `-- web/
+|       |-- __init__.py
+|       `-- root.py
+|-- test1.txt
+|-- test2.txt
+`-- twisted/
+    `-- plugins/
+        `-- testplugin.py"""
+        print "expected", expected
+        self.assertEquals(expected, s)

File txproject/test_factory.py

+import os
+import shutil
+import unittest
+
+from txproject import factory
+
+
+ROOT = os.path.join(
+    os.path.dirname(os.path.abspath(__file__)),
+    "testdirs"
+)
+
+
+class MockTemplate(object):
+    def __init__(self, directories=[], files=[]):
+        self._directories = directories
+        self._files = files
+
+    def directories(self):
+        return self._directories
+
+    def files(self):
+        return self._files
+
+
+class TestNewProjectFactory(unittest.TestCase):
+    def setUp(self):
+        self.f = factory.NewProject("testproject", "default", root=ROOT)
+        self.f.getTemplate = self.mockGetTemplate
+
+    def mockGetTemplate(self):
+        m = MockTemplate(directories=["bin",
+                                      "<project>",
+                                      "test",
+                                      "twisted/plugins"],
+                         files=[{"<project>/web.py":""},
+                                {"test/test_web.py":""},
+                                {"twisted/plugins/<project>_plugin.py":""}]
+                         )
+        return m
+
+    def test_projectName(self):
+        self.assertEquals("Testproject", self.f.projectName())
+
+    def test_directories(self):
+        dirs = self.f.directories()
+        self.assertEquals(["bin", "testproject", "test", "twisted/plugins"],
+                          dirs,
+                          "expect this list of dirs with substitutions")
+
+    def test_files(self):
+        files = self.f.files()
+        self.assertEquals(["testproject/web.py",
+                           "test/test_web.py",
+                           "twisted/plugins/testproject_plugin.py"],
+                           files,
+                          "expect this list of files with substitutions")
+
+
+class TestCreatingDirectories(unittest.TestCase):
+    def setUp(self):
+        self.tearDown()
+        os.makedirs(ROOT)
+        f = factory.NewProject("testproject", "default", root=ROOT)
+        f.getTemplate = self.mockGetTemplate
+        f.makeDirectories()
+
+    def mockGetTemplate(self):
+        m = MockTemplate(directories=["bin",
+                                      "<project>",
+                                      "test",
+                                      "twisted/plugins"],
+                         files=[])
+        return m
+
+    def tearDown(self):
+        if os.path.exists(ROOT):
+            shutil.rmtree(ROOT)
+
+    def test_bin(self):
+        self.assertTrue(os.path.exists(
+                os.path.join(ROOT, "Testproject", "bin")
+            ),
+            "project should contain a bin directory"        
+        )
+
+    def test_project(self):
+        self.assertTrue(os.path.exists(
+                os.path.join(ROOT, "Testproject", "testproject")
+            ),
+            "project should contain project subdirectory"
+        )
+
+    def test_testDir(self):
+        self.assertTrue(os.path.exists(
+                        os.path.join(ROOT, "Testproject", "test")
+                        ),
+                        "project should contain test directory"
+                        )
+
+    def test_plugins(self):
+        self.assertTrue(os.path.exists(
+            os.path.join(ROOT, "Testproject", "twisted", "plugins")
+            ),
+                        "project should contain twisted/plugins directory"
+                        )
+        
+
+class TestCreatingDirectoriesAndFiles(unittest.TestCase):
+    def setUp(self):
+        self.tearDown()
+        os.makedirs(ROOT)
+        newfile1 = open(os.path.join(ROOT, "testfile1.py"), "w")
+        newfile1.write("# __init__.py default Default 1\n")
+        newfile1.close()
+        newfile2 = open(os.path.join(ROOT, "testfile2.py"), "w")
+        newfile2.write("# __init__.py 2\n")
+        newfile2.close()
+        f = factory.NewProject("testproject", "default", root=ROOT, templateRoot=ROOT)
+        f.getTemplate = self.mockGetTemplate
+        f.makeDirectories()
+        f.makeFiles()
+
+    def mockGetTemplate(self):
+        m = MockTemplate(directories=["bin",
+                                      "<project>",
+                                      "test",
+                                      "twisted/plugins"],
+                         files=[{"<project>/web.py":"testfile1.py"},
+                                {"test/test_web.py":"testfile2.py"},
+                                {"twisted/plugins/<project>_plugin.py":"testfile1.py"}]
+                         )
+        return m
+
+    def tearDown(self):
+        if os.path.exists(ROOT):
+            shutil.rmtree(ROOT)
+
+    def test_tests(self):
+        self.assertTrue(
+            os.path.exists(
+                os.path.join(ROOT, "Testproject", "test", "test_web.py")
+            ),
+            "There should be a test_web.py file"
+        )
+
+    def test_web(self):
+        self.assertTrue(
+            os.path.exists(
+                os.path.join(ROOT, "Testproject",
+                             "testproject", "web.py")
+            ),
+            "There should be a web.py file"
+        )
+
+    def test_plugin(self):
+        self.assertTrue(
+            os.path.exists(
+                os.path.join(ROOT, "Testproject",
+                             "twisted", "plugins",
+                             "testproject_plugin.py")
+            ),
+            "There should be a web.py file"
+        )
+
+    def test_tests_contents(self):
+        f = open(os.path.join(ROOT, "Testproject", "test", "test_web.py"),"r")
+        contents = f.read()
+        self.assertEquals("# __init__.py 2\n", contents)
+
+    def test_web_contents_substitution(self):
+        f = open(os.path.join(ROOT, "Testproject",
+                     "testproject", "web.py"), "r")
+        contents = f.read()
+        self.assertEquals("# __init__.py testproject Testproject 1\n", contents)

File txproject/test_template.py

+import os
+import unittest
+
+from txproject import template
+
+ROOT = os.path.join(os.path.dirname(os.path.abspath(__file__)), "testingtemplates")
+
+class TestTemplateRegistry(unittest.TestCase):
+    def test_getEmptyTemplateRegistry(self):
+        r = template.TemplateRegistry(template_dir=None)
+        self.assertEquals(0, len(r.templates))
+
+    def test_getSpecificTemplateRegistry(self):
+        r = template.TemplateRegistry(template_dir=ROOT)
+        self.assertEquals(1, len(r.templates))
+        t = r.templates["test"]
+        self.assertEquals("test", t.name)
+
+    def test_getDefaultTemplateRegistry(self):
+        r = template.TemplateRegistry()
+        self.assertEquals(2, len(r.templates))
+        
+
+class TestTemplate(unittest.TestCase):
+    def setUp(self):
+        self.t = template.Template("test", {"directories":[],
+                                            "files":[]})
+
+    def test_name(self):
+        self.assertEquals("test", self.t.name)
+
+    def test_directories(self):
+        self.assertEquals([], self.t.directories())
+
+    def test_files(self):
+        self.assertEquals([], self.t.files())
+

File txproject/testingtemplates/index.json

+{"test":"test"
+ }