summaryrefslogtreecommitdiffstats
path: root/cobbler/Cheetah/Tests
diff options
context:
space:
mode:
Diffstat (limited to 'cobbler/Cheetah/Tests')
-rw-r--r--cobbler/Cheetah/Tests/CheetahWrapper.py596
-rw-r--r--cobbler/Cheetah/Tests/FileRefresh.py55
-rw-r--r--cobbler/Cheetah/Tests/NameMapper.py539
-rw-r--r--cobbler/Cheetah/Tests/SyntaxAndOutput.py3170
-rw-r--r--cobbler/Cheetah/Tests/Template.py312
-rw-r--r--cobbler/Cheetah/Tests/Test.py70
-rw-r--r--cobbler/Cheetah/Tests/__init__.py1
-rw-r--r--cobbler/Cheetah/Tests/unittest_local_copy.py977
8 files changed, 5720 insertions, 0 deletions
diff --git a/cobbler/Cheetah/Tests/CheetahWrapper.py b/cobbler/Cheetah/Tests/CheetahWrapper.py
new file mode 100644
index 0000000..316c43b
--- /dev/null
+++ b/cobbler/Cheetah/Tests/CheetahWrapper.py
@@ -0,0 +1,596 @@
+#!/usr/bin/env python
+# $Id: CheetahWrapper.py,v 1.5 2006/01/07 07:18:44 tavis_rudd Exp $
+"""Tests for the 'cheetah' command.
+
+Besides unittest usage, recognizes the following command-line options:
+ --list CheetahWrapper.py
+ List all scenarios that are tested. The argument is the path
+ of this script.
+ --nodelete
+ Don't delete scratch directory at end.
+ --output
+ Show the output of each subcommand. (Normally suppressed.)
+
+Meta-Data
+================================================================================
+Author: Mike Orr <iron@mso.oz.net>,
+Version: $Revision: 1.5 $
+Start Date: 2001/10/01
+Last Revision Date: $Date: 2006/01/07 07:18:44 $
+"""
+__author__ = "Mike Orr <iron@mso.oz.net>"
+__revision__ = "$Revision: 1.5 $"[11:-2]
+
+
+##################################################
+## DEPENDENCIES ##
+
+import commands, os, shutil, sys, tempfile
+import unittest_local_copy as unittest
+
+import re # Used by listTests.
+from Cheetah.CheetahWrapper import CheetahWrapper # Used by NoBackup.
+from Cheetah.Utils.optik import OptionParser # Used by main.
+
+##################################################
+## CONSTANTS & GLOBALS ##
+
+try:
+ True,False
+except NameError:
+ True, False = (1==1),(1==0)
+
+DELETE = True # True to clean up after ourselves, False for debugging.
+OUTPUT = False # Normally False, True for debugging.
+
+#DELETE = False # True to clean up after ourselves, False for debugging.
+#OUTPUT = True # Normally False, True for debugging.
+
+BACKUP_SUFFIX = CheetahWrapper.BACKUP_SUFFIX
+
+def warn(msg):
+ sys.stderr.write(msg + '\n')
+
+##################################################
+## TEST BASE CLASSES
+
+class CFBase(unittest.TestCase):
+ """Base class for "cheetah compile" and "cheetah fill" unit tests.
+ """
+ srcDir = '' # Nonblank to create source directory.
+ subdirs = ('child', 'child/grandkid') # Delete in reverse order.
+ srcFiles = ('a.tmpl', 'child/a.tmpl', 'child/grandkid/a.tmpl')
+ expectError = False # Used by --list option.
+
+ def inform(self, message):
+ if self.verbose:
+ print message
+
+ def setUp(self):
+ """Create the top-level directories, subdirectories and .tmpl
+ files.
+ """
+ I = self.inform
+ # Step 1: Create the scratch directory and chdir into it.
+ self.scratchDir = scratchDir = tempfile.mktemp()
+ os.mkdir(scratchDir)
+ self.origCwd = os.getcwd()
+ os.chdir(scratchDir)
+ if self.srcDir:
+ os.mkdir(self.srcDir)
+ # Step 2: Create source subdirectories.
+ for dir in self.subdirs:
+ os.mkdir(dir)
+ # Step 3: Create the .tmpl files, each in its proper directory.
+ for fil in self.srcFiles:
+ f = open(fil, 'w')
+ f.write("Hello, world!\n")
+ f.close()
+
+
+ def tearDown(self):
+ os.chdir(self.origCwd)
+ if DELETE:
+ shutil.rmtree(self.scratchDir, True) # Ignore errors.
+ if os.path.exists(self.scratchDir):
+ warn("Warning: unable to delete scratch directory %s")
+ else:
+ warn("Warning: not deleting scratch directory %s" % self.scratchDir)
+
+
+ def _checkDestFileHelper(self, path, expected,
+ allowSurroundingText, errmsg):
+ """Low-level helper to check a destination file.
+
+ in : path, string, the destination path.
+ expected, string, the expected contents.
+ allowSurroundingtext, bool, allow the result to contain
+ additional text around the 'expected' substring?
+ errmsg, string, the error message. It may contain the
+ following "%"-operator keys: path, expected, result.
+ out: None
+ """
+ path = os.path.abspath(path)
+ exists = os.path.exists(path)
+ msg = "destination file missing: %s" % path
+ self.failUnless(exists, msg)
+ f = open(path, 'r')
+ result = f.read()
+ f.close()
+ if allowSurroundingText:
+ success = result.find(expected) != -1
+ else:
+ success = result == expected
+ msg = errmsg % locals()
+ self.failUnless(success, msg)
+
+
+ def checkCompile(self, path):
+ # Raw string to prevent "\n" from being converted to a newline.
+ #expected = R"write('Hello, world!\n')"
+ expected = R"'Hello, world!\n')" # might output a u'' string
+ errmsg = """\
+destination file %(path)s doesn't contain expected substring:
+%(expected)r"""
+ self._checkDestFileHelper(path, expected, True, errmsg)
+
+
+ def checkFill(self, path):
+ expected = "Hello, world!\n"
+ errmsg = """\
+destination file %(path)s contains wrong result.
+Expected %(expected)r
+Found %(result)r"""
+ self._checkDestFileHelper(path, expected, False, errmsg)
+
+
+ def checkSubdirPyInit(self, path):
+ """Verify a destination subdirectory exists and contains an
+ __init__.py file.
+ """
+ exists = os.path.exists(path)
+ msg = "destination subdirectory %s misssing" % path
+ self.failUnless(exists, msg)
+ initPath = os.path.join(path, "__init__.py")
+ exists = os.path.exists(initPath)
+ msg = "destination init file missing: %s" % initPath
+ self.failUnless(exists, msg)
+
+
+ def checkNoBackup(self, path):
+ """Verify 'path' does not exist. (To check --nobackup.)
+ """
+ exists = os.path.exists(path)
+ msg = "backup file exists in spite of --nobackup: %s" % path
+ self.failIf(exists, msg)
+
+
+ def go(self, cmd, expectedStatus=0, expectedOutputSubstring=None):
+ """Run a "cheetah compile" or "cheetah fill" subcommand.
+
+ in : cmd, string, the command to run.
+ expectedStatus, int, subcommand's expected output status.
+ 0 if the subcommand is expected to succeed, 1-255 otherwise.
+ expectedOutputSubstring, string, substring which much appear
+ in the standard output or standard error. None to skip this
+ test.
+ out: None.
+ """
+ # Use commands.getstatusoutput instead of os.system so
+ # that we can mimic ">/dev/null 2>/dev/null" even on
+ # non-Unix platforms.
+ exit, output = commands.getstatusoutput(cmd)
+ status, signal = divmod(exit, 256)
+ if OUTPUT:
+ if output.endswith("\n"):
+ output = output[:-1]
+ print
+ print "SUBCOMMAND:", cmd
+ print output
+ print
+ msg = "subcommand killed by signal %d: %s" % (signal, cmd)
+ self.failUnlessEqual(signal, 0, msg)
+ msg = "subcommand exit status %d: %s" % (status, cmd)
+ if status!=expectedStatus:
+ print output
+ self.failUnlessEqual(status, expectedStatus, msg)
+ if expectedOutputSubstring is not None:
+ msg = "substring %r not found in subcommand output: %s" % \
+ (expectedOutputSubstring, cmd)
+ substringTest = output.find(expectedOutputSubstring) != -1
+ self.failUnless(substringTest, msg)
+
+
+ def goExpectError(self, cmd):
+ """Run a subcommand and expect it to fail.
+
+ in : cmd, string, the command to run.
+ out: None.
+ """
+ # Use commands.getstatusoutput instead of os.system so
+ # that we can mimic ">/dev/null 2>/dev/null" even on
+ # non-Unix platforms.
+ exit, output = commands.getstatusoutput(cmd)
+ status, signal = divmod(exit, 256)
+ msg = "subcommand killed by signal %s: %s" % (signal, cmd)
+ self.failUnlessEqual(signal, 0, msg) # Signal must be 0.
+ msg = "subcommand exit status %s: %s" % (status, cmd)
+ self.failIfEqual(status, 0, msg) # Status must *not* be 0.
+ if OUTPUT:
+ if output.endswith("\n"):
+ output = output[:-1]
+ print
+ print "SUBCOMMAND:", cmd
+ print output
+ print
+
+
+class CFIdirBase(CFBase):
+ """Subclass for tests with --idir.
+ """
+ srcDir = 'SRC'
+ subdirs = ('SRC/child', 'SRC/child/grandkid') # Delete in reverse order.
+ srcFiles = ('SRC/a.tmpl', 'SRC/child/a.tmpl', 'SRC/child/grandkid/a.tmpl')
+
+
+
+##################################################
+## TEST CASE CLASSES
+
+class OneFile(CFBase):
+ def testCompile(self):
+ self.go("cheetah compile a.tmpl")
+ self.checkCompile("a.py")
+
+ def testFill(self):
+ self.go("cheetah fill a.tmpl")
+ self.checkFill("a.html")
+
+ def testText(self):
+ self.go("cheetah fill --oext txt a.tmpl")
+ self.checkFill("a.txt")
+
+
+class OneFileNoExtension(CFBase):
+ def testCompile(self):
+ self.go("cheetah compile a")
+ self.checkCompile("a.py")
+
+ def testFill(self):
+ self.go("cheetah fill a")
+ self.checkFill("a.html")
+
+ def testText(self):
+ self.go("cheetah fill --oext txt a")
+ self.checkFill("a.txt")
+
+
+class SplatTmpl(CFBase):
+ def testCompile(self):
+ self.go("cheetah compile *.tmpl")
+ self.checkCompile("a.py")
+
+ def testFill(self):
+ self.go("cheetah fill *.tmpl")
+ self.checkFill("a.html")
+
+ def testText(self):
+ self.go("cheetah fill --oext txt *.tmpl")
+ self.checkFill("a.txt")
+
+class ThreeFilesWithSubdirectories(CFBase):
+ def testCompile(self):
+ self.go("cheetah compile a.tmpl child/a.tmpl child/grandkid/a.tmpl")
+ self.checkCompile("a.py")
+ self.checkCompile("child/a.py")
+ self.checkCompile("child/grandkid/a.py")
+
+ def testFill(self):
+ self.go("cheetah fill a.tmpl child/a.tmpl child/grandkid/a.tmpl")
+ self.checkFill("a.html")
+ self.checkFill("child/a.html")
+ self.checkFill("child/grandkid/a.html")
+
+ def testText(self):
+ self.go("cheetah fill --oext txt a.tmpl child/a.tmpl child/grandkid/a.tmpl")
+ self.checkFill("a.txt")
+ self.checkFill("child/a.txt")
+ self.checkFill("child/grandkid/a.txt")
+
+
+class ThreeFilesWithSubdirectoriesNoExtension(CFBase):
+ def testCompile(self):
+ self.go("cheetah compile a child/a child/grandkid/a")
+ self.checkCompile("a.py")
+ self.checkCompile("child/a.py")
+ self.checkCompile("child/grandkid/a.py")
+
+ def testFill(self):
+ self.go("cheetah fill a child/a child/grandkid/a")
+ self.checkFill("a.html")
+ self.checkFill("child/a.html")
+ self.checkFill("child/grandkid/a.html")
+
+ def testText(self):
+ self.go("cheetah fill --oext txt a child/a child/grandkid/a")
+ self.checkFill("a.txt")
+ self.checkFill("child/a.txt")
+ self.checkFill("child/grandkid/a.txt")
+
+
+class SplatTmplWithSubdirectories(CFBase):
+ def testCompile(self):
+ self.go("cheetah compile *.tmpl child/*.tmpl child/grandkid/*.tmpl")
+ self.checkCompile("a.py")
+ self.checkCompile("child/a.py")
+ self.checkCompile("child/grandkid/a.py")
+
+ def testFill(self):
+ self.go("cheetah fill *.tmpl child/*.tmpl child/grandkid/*.tmpl")
+ self.checkFill("a.html")
+ self.checkFill("child/a.html")
+ self.checkFill("child/grandkid/a.html")
+
+ def testText(self):
+ self.go("cheetah fill --oext txt *.tmpl child/*.tmpl child/grandkid/*.tmpl")
+ self.checkFill("a.txt")
+ self.checkFill("child/a.txt")
+ self.checkFill("child/grandkid/a.txt")
+
+
+class OneFileWithOdir(CFBase):
+ def testCompile(self):
+ self.go("cheetah compile --odir DEST a.tmpl")
+ self.checkSubdirPyInit("DEST")
+ self.checkCompile("DEST/a.py")
+
+ def testFill(self):
+ self.go("cheetah fill --odir DEST a.tmpl")
+ self.checkFill("DEST/a.html")
+
+ def testText(self):
+ self.go("cheetah fill --odir DEST --oext txt a.tmpl")
+ self.checkFill("DEST/a.txt")
+
+
+class VarietyWithOdir(CFBase):
+ def testCompile(self):
+ self.go("cheetah compile --odir DEST a.tmpl child/a child/grandkid/*.tmpl")
+ self.checkSubdirPyInit("DEST")
+ self.checkSubdirPyInit("DEST/child")
+ self.checkSubdirPyInit("DEST/child/grandkid")
+ self.checkCompile("DEST/a.py")
+ self.checkCompile("DEST/child/a.py")
+ self.checkCompile("DEST/child/grandkid/a.py")
+
+ def testFill(self):
+ self.go("cheetah fill --odir DEST a.tmpl child/a child/grandkid/*.tmpl")
+ self.checkFill("DEST/a.html")
+ self.checkFill("DEST/child/a.html")
+ self.checkFill("DEST/child/grandkid/a.html")
+
+ def testText(self):
+ self.go("cheetah fill --odir DEST --oext txt a.tmpl child/a child/grandkid/*.tmpl")
+ self.checkFill("DEST/a.txt")
+ self.checkFill("DEST/child/a.txt")
+ self.checkFill("DEST/child/grandkid/a.txt")
+
+
+class RecurseExplicit(CFBase):
+ def testCompile(self):
+ self.go("cheetah compile -R child")
+ self.checkCompile("child/a.py")
+ self.checkCompile("child/grandkid/a.py")
+
+ def testFill(self):
+ self.go("cheetah fill -R child")
+ self.checkFill("child/a.html")
+ self.checkFill("child/grandkid/a.html")
+
+ def testText(self):
+ self.go("cheetah fill -R --oext txt child")
+ self.checkFill("child/a.txt")
+ self.checkFill("child/grandkid/a.txt")
+
+
+class RecurseImplicit(CFBase):
+ def testCompile(self):
+ self.go("cheetah compile -R")
+ self.checkCompile("child/a.py")
+ self.checkCompile("child/grandkid/a.py")
+
+ def testFill(self):
+ self.go("cheetah fill -R")
+ self.checkFill("a.html")
+ self.checkFill("child/a.html")
+ self.checkFill("child/grandkid/a.html")
+
+ def testText(self):
+ self.go("cheetah fill -R --oext txt")
+ self.checkFill("a.txt")
+ self.checkFill("child/a.txt")
+ self.checkFill("child/grandkid/a.txt")
+
+
+class RecurseExplicitWIthOdir(CFBase):
+ def testCompile(self):
+ self.go("cheetah compile -R --odir DEST child")
+ self.checkSubdirPyInit("DEST/child")
+ self.checkSubdirPyInit("DEST/child/grandkid")
+ self.checkCompile("DEST/child/a.py")
+ self.checkCompile("DEST/child/grandkid/a.py")
+
+ def testFill(self):
+ self.go("cheetah fill -R --odir DEST child")
+ self.checkFill("DEST/child/a.html")
+ self.checkFill("DEST/child/grandkid/a.html")
+
+ def testText(self):
+ self.go("cheetah fill -R --odir DEST --oext txt child")
+ self.checkFill("DEST/child/a.txt")
+ self.checkFill("DEST/child/grandkid/a.txt")
+
+
+class Flat(CFBase):
+ def testCompile(self):
+ self.go("cheetah compile --flat child/a.tmpl")
+ self.checkCompile("a.py")
+
+ def testFill(self):
+ self.go("cheetah fill --flat child/a.tmpl")
+ self.checkFill("a.html")
+
+ def testText(self):
+ self.go("cheetah fill --flat --oext txt child/a.tmpl")
+ self.checkFill("a.txt")
+
+
+class FlatRecurseCollision(CFBase):
+ expectError = True
+
+ def testCompile(self):
+ self.goExpectError("cheetah compile -R --flat")
+
+ def testFill(self):
+ self.goExpectError("cheetah fill -R --flat")
+
+ def testText(self):
+ self.goExpectError("cheetah fill -R --flat")
+
+
+class IdirRecurse(CFIdirBase):
+ def testCompile(self):
+ self.go("cheetah compile -R --idir SRC child")
+ self.checkSubdirPyInit("child")
+ self.checkSubdirPyInit("child/grandkid")
+ self.checkCompile("child/a.py")
+ self.checkCompile("child/grandkid/a.py")
+
+ def testFill(self):
+ self.go("cheetah fill -R --idir SRC child")
+ self.checkFill("child/a.html")
+ self.checkFill("child/grandkid/a.html")
+
+ def testText(self):
+ self.go("cheetah fill -R --idir SRC --oext txt child")
+ self.checkFill("child/a.txt")
+ self.checkFill("child/grandkid/a.txt")
+
+
+class IdirOdirRecurse(CFIdirBase):
+ def testCompile(self):
+ self.go("cheetah compile -R --idir SRC --odir DEST child")
+ self.checkSubdirPyInit("DEST/child")
+ self.checkSubdirPyInit("DEST/child/grandkid")
+ self.checkCompile("DEST/child/a.py")
+ self.checkCompile("DEST/child/grandkid/a.py")
+
+ def testFill(self):
+ self.go("cheetah fill -R --idir SRC --odir DEST child")
+ self.checkFill("DEST/child/a.html")
+ self.checkFill("DEST/child/grandkid/a.html")
+
+ def testText(self):
+ self.go("cheetah fill -R --idir SRC --odir DEST --oext txt child")
+ self.checkFill("DEST/child/a.txt")
+ self.checkFill("DEST/child/grandkid/a.txt")
+
+
+class IdirFlatRecurseCollision(CFIdirBase):
+ expectError = True
+
+ def testCompile(self):
+ self.goExpectError("cheetah compile -R --flat --idir SRC")
+
+ def testFill(self):
+ self.goExpectError("cheetah fill -R --flat --idir SRC")
+
+ def testText(self):
+ self.goExpectError("cheetah fill -R --flat --idir SRC --oext txt")
+
+
+class NoBackup(CFBase):
+ """Run the command twice each time and verify a backup file is
+ *not* created.
+ """
+ def testCompile(self):
+ self.go("cheetah compile --nobackup a.tmpl")
+ self.go("cheetah compile --nobackup a.tmpl")
+ self.checkNoBackup("a.py" + BACKUP_SUFFIX)
+
+ def testFill(self):
+ self.go("cheetah fill --nobackup a.tmpl")
+ self.go("cheetah fill --nobackup a.tmpl")
+ self.checkNoBackup("a.html" + BACKUP_SUFFIX)
+
+ def testText(self):
+ self.go("cheetah fill --nobackup --oext txt a.tmpl")
+ self.go("cheetah fill --nobackup --oext txt a.tmpl")
+ self.checkNoBackup("a.txt" + BACKUP_SUFFIX)
+
+
+
+
+
+##################################################
+## LIST TESTS ##
+
+def listTests(cheetahWrapperFile):
+ """cheetahWrapperFile, string, path of this script.
+
+ XXX TODO: don't print test where expectError is true.
+ """
+ rx = re.compile( R'self\.go\("(.*?)"\)' )
+ f = open(cheetahWrapperFile)
+ while 1:
+ lin = f.readline()
+ if not lin:
+ break
+ m = rx.search(lin)
+ if m:
+ print m.group(1)
+ f.close()
+
+##################################################
+## MAIN ROUTINE ##
+
+class MyOptionParser(OptionParser):
+ """Disable the standard --help and --verbose options since
+ --help is used for another purpose.
+ """
+ standard_option_list = []
+
+def main():
+ global DELETE, OUTPUT
+ parser = MyOptionParser()
+ parser.add_option("--list", action="store", dest="listTests")
+ parser.add_option("--nodelete", action="store_true")
+ parser.add_option("--output", action="store_true")
+ # The following options are passed to unittest.
+ parser.add_option("-e", "--explain", action="store_true")
+ parser.add_option("-h", "--help", action="store_true")
+ parser.add_option("-v", "--verbose", action="store_true")
+ parser.add_option("-q", "--quiet", action="store_true")
+ opts, files = parser.parse_args()
+ if opts.nodelete:
+ DELETE = False
+ if opts.output:
+ OUTPUT = True
+ if opts.listTests:
+ listTests(opts.listTests)
+ else:
+ # Eliminate script-specific command-line arguments to prevent
+ # errors in unittest.
+ del sys.argv[1:]
+ for opt in ("explain", "help", "verbose", "quiet"):
+ if getattr(opts, opt):
+ sys.argv.append("--" + opt)
+ sys.argv.extend(files)
+ unittest.main()
+
+##################################################
+## if run from the command line ##
+
+if __name__ == '__main__': main()
+
+# vim: sw=4 ts=4 expandtab
diff --git a/cobbler/Cheetah/Tests/FileRefresh.py b/cobbler/Cheetah/Tests/FileRefresh.py
new file mode 100644
index 0000000..4beb3e7
--- /dev/null
+++ b/cobbler/Cheetah/Tests/FileRefresh.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python
+# $Id: FileRefresh.py,v 1.6 2002/10/01 17:52:03 tavis_rudd Exp $
+"""Tests to make sure that the file-update-monitoring code is working properly
+
+THIS TEST MODULE IS JUST A SHELL AT THE MOMENT. Feel like filling it in??
+
+Meta-Data
+================================================================================
+Author: Tavis Rudd <tavis@damnsimple.com>,
+Version: $Revision: 1.6 $
+Start Date: 2001/10/01
+Last Revision Date: $Date: 2002/10/01 17:52:03 $
+"""
+__author__ = "Tavis Rudd <tavis@damnsimple.com>"
+__revision__ = "$Revision: 1.6 $"[11:-2]
+
+
+##################################################
+## DEPENDENCIES ##
+
+import sys
+import types
+import os
+import os.path
+
+
+import unittest_local_copy as unittest
+from Cheetah.Template import Template
+
+##################################################
+## CONSTANTS & GLOBALS ##
+
+try:
+ True,False
+except NameError:
+ True, False = (1==1),(1==0)
+
+##################################################
+## TEST DATA FOR USE IN THE TEMPLATES ##
+
+##################################################
+## TEST BASE CLASSES
+
+class TemplateTest(unittest.TestCase):
+ pass
+
+##################################################
+## TEST CASE CLASSES
+
+
+##################################################
+## if run from the command line ##
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/cobbler/Cheetah/Tests/NameMapper.py b/cobbler/Cheetah/Tests/NameMapper.py
new file mode 100644
index 0000000..2782463
--- /dev/null
+++ b/cobbler/Cheetah/Tests/NameMapper.py
@@ -0,0 +1,539 @@
+#!/usr/bin/env python
+# $Id: NameMapper.py,v 1.11 2006/01/15 20:45:22 tavis_rudd Exp $
+"""NameMapper Tests
+
+Meta-Data
+================================================================================
+Author: Tavis Rudd <tavis@damnsimple.com>,
+Version: $Revision: 1.11 $
+Start Date: 2001/10/01
+Last Revision Date: $Date: 2006/01/15 20:45:22 $
+"""
+from __future__ import generators
+__author__ = "Tavis Rudd <tavis@damnsimple.com>"
+__revision__ = "$Revision: 1.11 $"[11:-2]
+import sys
+import types
+import os
+import os.path
+
+import unittest_local_copy as unittest
+from Cheetah.NameMapper import NotFound, valueForKey, \
+ valueForName, valueFromSearchList, valueFromFrame, valueFromFrameOrSearchList
+
+
+##################################################
+## TEST DATA FOR USE IN THE TEMPLATES ##
+
+class DummyClass:
+ classVar1 = 123
+
+ def __init__(self):
+ self.instanceVar1 = 123
+
+ def __str__(self):
+ return 'object'
+
+ def meth(self, arg="arff"):
+ return str(arg)
+
+ def meth1(self, arg="doo"):
+ return arg
+
+ def meth2(self, arg1="a1", arg2="a2"):
+ raise ValueError
+
+ def meth3(self):
+ """Tests a bug that Jeff Johnson reported on Oct 1, 2001"""
+
+ x = 'A string'
+ try:
+ for i in [1,2,3,4]:
+ if x == 2:
+ pass
+
+ if x == 'xx':
+ pass
+ return x
+ except:
+ raise
+
+
+def dummyFunc(arg="Scooby"):
+ return arg
+
+def funcThatRaises():
+ raise ValueError
+
+
+testNamespace = {
+ 'aStr':'blarg',
+ 'anInt':1,
+ 'aFloat':1.5,
+ 'aDict': {'one':'item1',
+ 'two':'item2',
+ 'nestedDict':{'one':'nestedItem1',
+ 'two':'nestedItem2',
+ 'funcThatRaises':funcThatRaises,
+ 'aClass': DummyClass,
+ },
+ 'nestedFunc':dummyFunc,
+ },
+ 'aClass': DummyClass,
+ 'aFunc': dummyFunc,
+ 'anObj': DummyClass(),
+ 'aMeth': DummyClass().meth1,
+ 'none' : None,
+ 'emptyString':'',
+ 'funcThatRaises':funcThatRaises,
+ }
+
+autoCallResults = {'aFunc':'Scooby',
+ 'aMeth':'doo',
+ }
+
+results = testNamespace.copy()
+results.update({'anObj.meth1':'doo',
+ 'aDict.one':'item1',
+ 'aDict.nestedDict':testNamespace['aDict']['nestedDict'],
+ 'aDict.nestedDict.one':'nestedItem1',
+ 'aDict.nestedDict.aClass':DummyClass,
+ 'aDict.nestedFunc':'Scooby',
+ 'aClass.classVar1':123,
+ 'anObj.instanceVar1':123,
+ 'anObj.meth3':'A string',
+ })
+
+for k in testNamespace.keys():
+ # put them in the globals for the valueFromFrame tests
+ exec '%s = testNamespace[k]'%k
+
+##################################################
+## TEST BASE CLASSES
+
+class NameMapperTest(unittest.TestCase):
+ failureException = (NotFound,AssertionError)
+ _testNamespace = testNamespace
+ _results = results
+
+ def namespace(self):
+ return self._testNamespace
+
+ def VFN(self, name, autocall=True):
+ return valueForName(self.namespace(), name, autocall)
+
+ def VFS(self, searchList, name, autocall=True):
+ return valueFromSearchList(searchList, name, autocall)
+
+
+ # alias to be overriden later
+ get = VFN
+
+ def check(self, name):
+ got = self.get(name)
+ if autoCallResults.has_key(name):
+ expected = autoCallResults[name]
+ else:
+ expected = self._results[name]
+ assert got == expected
+
+
+##################################################
+## TEST CASE CLASSES
+
+class VFN(NameMapperTest):
+
+ def test1(self):
+ """string in dict lookup"""
+ self.check('aStr')
+
+ def test2(self):
+ """string in dict lookup in a loop"""
+ for i in range(10):
+ self.check('aStr')
+
+ def test3(self):
+ """int in dict lookup"""
+ self.check('anInt')
+
+ def test4(self):
+ """int in dict lookup in a loop"""
+ for i in range(10):
+ self.check('anInt')
+
+ def test5(self):
+ """float in dict lookup"""
+ self.check('aFloat')
+
+ def test6(self):
+ """float in dict lookup in a loop"""
+ for i in range(10):
+ self.check('aFloat')
+
+ def test7(self):
+ """class in dict lookup"""
+ self.check('aClass')
+
+ def test8(self):
+ """class in dict lookup in a loop"""
+ for i in range(10):
+ self.check('aClass')
+
+ def test9(self):
+ """aFunc in dict lookup"""
+ self.check('aFunc')
+
+ def test10(self):
+ """aFunc in dict lookup in a loop"""
+ for i in range(10):
+ self.check('aFunc')
+
+ def test11(self):
+ """aMeth in dict lookup"""
+ self.check('aMeth')
+
+ def test12(self):
+ """aMeth in dict lookup in a loop"""
+ for i in range(10):
+ self.check('aMeth')
+
+ def test13(self):
+ """aMeth in dict lookup"""
+ self.check('aMeth')
+
+ def test14(self):
+ """aMeth in dict lookup in a loop"""
+ for i in range(10):
+ self.check('aMeth')
+
+ def test15(self):
+ """anObj in dict lookup"""
+ self.check('anObj')
+
+ def test16(self):
+ """anObj in dict lookup in a loop"""
+ for i in range(10):
+ self.check('anObj')
+
+ def test17(self):
+ """aDict in dict lookup"""
+ self.check('aDict')
+
+ def test18(self):
+ """aDict in dict lookup in a loop"""
+ for i in range(10):
+ self.check('aDict')
+
+ def test17(self):
+ """aDict in dict lookup"""
+ self.check('aDict')
+
+ def test18(self):
+ """aDict in dict lookup in a loop"""
+ for i in range(10):
+ self.check('aDict')
+
+ def test19(self):
+ """aClass.classVar1 in dict lookup"""
+ self.check('aClass.classVar1')
+
+ def test20(self):
+ """aClass.classVar1 in dict lookup in a loop"""
+ for i in range(10):
+ self.check('aClass.classVar1')
+
+
+ def test23(self):
+ """anObj.instanceVar1 in dict lookup"""
+ self.check('anObj.instanceVar1')
+
+ def test24(self):
+ """anObj.instanceVar1 in dict lookup in a loop"""
+ for i in range(10):
+ self.check('anObj.instanceVar1')
+
+ ## tests 22, 25, and 26 removed when the underscored lookup was removed
+
+ def test27(self):
+ """anObj.meth1 in dict lookup"""
+ self.check('anObj.meth1')
+
+ def test28(self):
+ """anObj.meth1 in dict lookup in a loop"""
+ for i in range(10):
+ self.check('anObj.meth1')
+
+ def test29(self):
+ """aDict.one in dict lookup"""
+ self.check('aDict.one')
+
+ def test30(self):
+ """aDict.one in dict lookup in a loop"""
+ for i in range(10):
+ self.check('aDict.one')
+
+ def test31(self):
+ """aDict.nestedDict in dict lookup"""
+ self.check('aDict.nestedDict')
+
+ def test32(self):
+ """aDict.nestedDict in dict lookup in a loop"""
+ for i in range(10):
+ self.check('aDict.nestedDict')
+
+ def test33(self):
+ """aDict.nestedDict.one in dict lookup"""
+ self.check('aDict.nestedDict.one')
+
+ def test34(self):
+ """aDict.nestedDict.one in dict lookup in a loop"""
+ for i in range(10):
+ self.check('aDict.nestedDict.one')
+
+ def test35(self):
+ """aDict.nestedFunc in dict lookup"""
+ self.check('aDict.nestedFunc')
+
+ def test36(self):
+ """aDict.nestedFunc in dict lookup in a loop"""
+ for i in range(10):
+ self.check('aDict.nestedFunc')
+
+ def test37(self):
+ """aDict.nestedFunc in dict lookup - without autocalling"""
+ assert self.get('aDict.nestedFunc', False) == dummyFunc
+
+ def test38(self):
+ """aDict.nestedFunc in dict lookup in a loop - without autocalling"""
+ for i in range(10):
+ assert self.get('aDict.nestedFunc', False) == dummyFunc
+
+ def test39(self):
+ """aMeth in dict lookup - without autocalling"""
+ assert self.get('aMeth', False) == self.namespace()['aMeth']
+
+ def test40(self):
+ """aMeth in dict lookup in a loop - without autocalling"""
+ for i in range(10):
+ assert self.get('aMeth', False) == self.namespace()['aMeth']
+
+ def test41(self):
+ """anObj.meth3 in dict lookup"""
+ self.check('anObj.meth3')
+
+ def test42(self):
+ """aMeth in dict lookup in a loop"""
+ for i in range(10):
+ self.check('anObj.meth3')
+
+ def test43(self):
+ """NotFound test"""
+
+ def test(self=self):
+ self.get('anObj.methX')
+ self.assertRaises(NotFound,test)
+
+ def test44(self):
+ """NotFound test in a loop"""
+ def test(self=self):
+ self.get('anObj.methX')
+
+ for i in range(10):
+ self.assertRaises(NotFound,test)
+
+ def test45(self):
+ """Other exception from meth test"""
+
+ def test(self=self):
+ self.get('anObj.meth2')
+ self.assertRaises(ValueError, test)
+
+ def test46(self):
+ """Other exception from meth test in a loop"""
+ def test(self=self):
+ self.get('anObj.meth2')
+
+ for i in range(10):
+ self.assertRaises(ValueError,test)
+
+ def test47(self):
+ """None in dict lookup"""
+ self.check('none')
+
+ def test48(self):
+ """None in dict lookup in a loop"""
+ for i in range(10):
+ self.check('none')
+
+ def test49(self):
+ """EmptyString in dict lookup"""
+ self.check('emptyString')
+
+ def test50(self):
+ """EmptyString in dict lookup in a loop"""
+ for i in range(10):
+ self.check('emptyString')
+
+ def test51(self):
+ """Other exception from func test"""
+
+ def test(self=self):
+ self.get('funcThatRaises')
+ self.assertRaises(ValueError, test)
+
+ def test52(self):
+ """Other exception from func test in a loop"""
+ def test(self=self):
+ self.get('funcThatRaises')
+
+ for i in range(10):
+ self.assertRaises(ValueError,test)
+
+
+ def test53(self):
+ """Other exception from func test"""
+
+ def test(self=self):
+ self.get('aDict.nestedDict.funcThatRaises')
+ self.assertRaises(ValueError, test)
+
+ def test54(self):
+ """Other exception from func test in a loop"""
+ def test(self=self):
+ self.get('aDict.nestedDict.funcThatRaises')
+
+ for i in range(10):
+ self.assertRaises(ValueError,test)
+
+ def test55(self):
+ """aDict.nestedDict.aClass in dict lookup"""
+ self.check('aDict.nestedDict.aClass')
+
+ def test56(self):
+ """aDict.nestedDict.aClass in dict lookup in a loop"""
+ for i in range(10):
+ self.check('aDict.nestedDict.aClass')
+
+ def test57(self):
+ """aDict.nestedDict.aClass in dict lookup - without autocalling"""
+ assert self.get('aDict.nestedDict.aClass', False) == DummyClass
+
+ def test58(self):
+ """aDict.nestedDict.aClass in dict lookup in a loop - without autocalling"""
+ for i in range(10):
+ assert self.get('aDict.nestedDict.aClass', False) == DummyClass
+
+ def test59(self):
+ """Other exception from func test -- but without autocalling shouldn't raise"""
+
+ self.get('aDict.nestedDict.funcThatRaises', False)
+
+ def test60(self):
+ """Other exception from func test in a loop -- but without autocalling shouldn't raise"""
+
+ for i in range(10):
+ self.get('aDict.nestedDict.funcThatRaises', False)
+
+class VFS(VFN):
+ _searchListLength = 1
+
+ def searchList(self):
+ lng = self._searchListLength
+ if lng == 1:
+ return [self.namespace()]
+ elif lng == 2:
+ return [self.namespace(),{'dummy':1234}]
+ elif lng == 3:
+ # a tuple for kicks
+ return ({'dummy':1234}, self.namespace(),{'dummy':1234})
+ elif lng == 4:
+ # a generator for more kicks
+ return self.searchListGenerator()
+
+ def searchListGenerator(self):
+ class Test:
+ pass
+ for i in [Test(),{'dummy':1234}, self.namespace(),{'dummy':1234}]:
+ yield i
+
+ def get(self, name, autocall=True):
+ return self.VFS(self.searchList(), name, autocall)
+
+class VFS_2namespaces(VFS):
+ _searchListLength = 2
+
+class VFS_3namespaces(VFS):
+ _searchListLength = 3
+
+class VFS_4namespaces(VFS):
+ _searchListLength = 4
+
+class VFF(VFN):
+ def get(self, name, autocall=True):
+ ns = self._testNamespace
+ aStr = ns['aStr']
+ aFloat = ns['aFloat']
+ none = 'some'
+ return valueFromFrame(name, autocall)
+
+ def setUp(self):
+ """Mod some of the data
+ """
+ self._testNamespace = ns = self._testNamespace.copy()
+ self._results = res = self._results.copy()
+ ns['aStr'] = res['aStr'] = 'BLARG'
+ ns['aFloat'] = res['aFloat'] = 0.1234
+ res['none'] = 'some'
+ res['True'] = True
+ res['False'] = False
+ res['None'] = None
+ res['eval'] = eval
+
+ def test_VFF_1(self):
+ """Builtins"""
+ self.check('True')
+ self.check('None')
+ self.check('False')
+ assert self.get('eval', False)==eval
+ assert self.get('range', False)==range
+
+class VFFSL(VFS):
+ _searchListLength = 1
+
+ def setUp(self):
+ """Mod some of the data
+ """
+ self._testNamespace = ns = self._testNamespace.copy()
+ self._results = res = self._results.copy()
+ ns['aStr'] = res['aStr'] = 'BLARG'
+ ns['aFloat'] = res['aFloat'] = 0.1234
+ res['none'] = 'some'
+
+ del ns['anInt'] # will be picked up by globals
+
+ def VFFSL(self, searchList, name, autocall=True):
+ anInt = 1
+ none = 'some'
+ return valueFromFrameOrSearchList(searchList, name, autocall)
+
+ def get(self, name, autocall=True):
+ return self.VFFSL(self.searchList(), name, autocall)
+
+class VFFSL_2(VFFSL):
+ _searchListLength = 2
+
+class VFFSL_3(VFFSL):
+ _searchListLength = 3
+
+class VFFSL_4(VFFSL):
+ _searchListLength = 4
+
+if sys.platform.startswith('java'):
+ del VFF, VFFSL, VFFSL_2, VFFSL_3, VFFSL_4
+
+
+##################################################
+## if run from the command line ##
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/cobbler/Cheetah/Tests/SyntaxAndOutput.py b/cobbler/Cheetah/Tests/SyntaxAndOutput.py
new file mode 100644
index 0000000..09abc4d
--- /dev/null
+++ b/cobbler/Cheetah/Tests/SyntaxAndOutput.py
@@ -0,0 +1,3170 @@
+#!/usr/bin/env python
+# $Id: SyntaxAndOutput.py,v 1.105 2006/06/21 23:48:19 tavis_rudd Exp $
+"""Syntax and Output tests.
+
+TODO
+- #finally
+- #filter
+- #errorCatcher
+- #echo
+- #silent
+
+Meta-Data
+================================================================================
+Author: Tavis Rudd <tavis@damnsimple.com>
+Version: $Revision: 1.105 $
+Start Date: 2001/03/30
+Last Revision Date: $Date: 2006/06/21 23:48:19 $
+"""
+__author__ = "Tavis Rudd <tavis@damnsimple.com>"
+__revision__ = "$Revision: 1.105 $"[11:-2]
+
+
+##################################################
+## DEPENDENCIES ##
+
+import sys
+import types
+import re
+from copy import deepcopy
+import os
+import os.path
+import new
+import warnings
+
+from Cheetah.NameMapper import NotFound
+from Cheetah.NameMapper import C_VERSION as NameMapper_C_VERSION
+from Cheetah.Template import Template
+from Cheetah.Parser import ParseError
+from Cheetah.Compiler import Compiler, DEFAULT_COMPILER_SETTINGS
+import unittest_local_copy as unittest
+
+class Unspecified: pass
+##################################################
+## CONSTANTS & GLOBALS ##
+
+majorVer, minorVer = sys.version_info[0], sys.version_info[1]
+versionTuple = (majorVer, minorVer)
+
+try:
+ True,False
+except NameError:
+ True, False = (1==1),(1==0)
+
+##################################################
+## TEST DATA FOR USE IN THE TEMPLATES ##
+
+def testdecorator(func):
+ return func
+
+class DummyClass:
+ _called = False
+ def __str__(self):
+ return 'object'
+
+ def meth(self, arg="arff"):
+ return str(arg)
+
+ def meth1(self, arg="doo"):
+ return arg
+
+ def meth2(self, arg1="a1", arg2="a2"):
+ return str(arg1) + str(arg2)
+
+ def methWithPercentSignDefaultArg(self, arg1="110%"):
+ return str(arg1)
+
+ def callIt(self, arg=1234):
+ self._called = True
+ self._callArg = arg
+
+
+def dummyFunc(arg="Scooby"):
+ return arg
+
+defaultTestNameSpace = {
+ 'aStr':'blarg',
+ 'anInt':1,
+ 'aFloat':1.5,
+ 'aList': ['item0','item1','item2'],
+ 'aDict': {'one':'item1',
+ 'two':'item2',
+ 'nestedDict':{1:'nestedItem1',
+ 'two':'nestedItem2'
+ },
+ 'nestedFunc':dummyFunc,
+ },
+ 'aFunc': dummyFunc,
+ 'anObj': DummyClass(),
+ 'aMeth': DummyClass().meth1,
+ 'aStrToBeIncluded': "$aStr $anInt",
+ 'none' : None,
+ 'emptyString':'',
+ 'numOne':1,
+ 'numTwo':2,
+ 'zero':0,
+ 'tenDigits': 1234567890,
+ 'webSafeTest': 'abc <=> &',
+ 'strip1': ' \t strippable whitespace \t\t \n',
+ 'strip2': ' \t strippable whitespace \t\t ',
+ 'strip3': ' \t strippable whitespace \t\t\n1 2 3\n',
+
+ 'blockToBeParsed':"""$numOne $numTwo""",
+ 'includeBlock2':"""$numOne $numTwo $aSetVar""",
+
+ 'includeFileName':'parseTest.txt',
+ 'listOfLambdas':[lambda x: x, lambda x: x, lambda x: x,],
+ 'list': [
+ {'index': 0, 'numOne': 1, 'numTwo': 2},
+ {'index': 1, 'numOne': 1, 'numTwo': 2},
+ ],
+ 'nameList': [('john', 'doe'), ('jane', 'smith')],
+ 'letterList': ['a', 'b', 'c'],
+ '_': lambda x: 'Translated: ' + x,
+ 'unicodeData':u'aoeu12345\u1234',
+ }
+
+
+##################################################
+## TEST BASE CLASSES
+
+class OutputTest(unittest.TestCase):
+ report = '''
+Template output mismatch:
+
+ Input Template =
+%(template)s%(end)s
+
+ Expected Output =
+%(expected)s%(end)s
+
+ Actual Output =
+%(actual)s%(end)s'''
+
+ convertEOLs = True
+ _EOLreplacement = None
+ _debugEOLReplacement = False
+
+ DEBUGLEV = 0
+ _searchList = [defaultTestNameSpace]
+
+ _useNewStyleCompilation = True
+ #_useNewStyleCompilation = False
+
+ _extraCompileKwArgs = None
+
+ def searchList(self):
+ return self._searchList
+
+ def verify(self, input, expectedOutput,
+ inputEncoding=None,
+ outputEncoding=None,
+ convertEOLs=Unspecified):
+ if self._EOLreplacement:
+ if convertEOLs is Unspecified:
+ convertEOLs = self.convertEOLs
+ if convertEOLs:
+ input = input.replace('\n', self._EOLreplacement)
+ expectedOutput = expectedOutput.replace('\n', self._EOLreplacement)
+
+ self._input = input
+ if self._useNewStyleCompilation:
+ extraKwArgs = self._extraCompileKwArgs or {}
+
+ templateClass = Template.compile(
+ source=input,
+ compilerSettings=self._getCompilerSettings(),
+ keepRefToGeneratedCode=True,
+ **extraKwArgs
+ )
+ moduleCode = templateClass._CHEETAH_generatedModuleCode
+ self.template = templateObj = templateClass(searchList=self.searchList())
+ else:
+ self.template = templateObj = Template(
+ input,
+ searchList=self.searchList(),
+ compilerSettings=self._getCompilerSettings(),
+ )
+ moduleCode = templateObj._CHEETAH_generatedModuleCode
+ if self.DEBUGLEV >= 1:
+ print moduleCode
+ try:
+ try:
+ output = templateObj.respond() # rather than __str__, because of unicode
+ if outputEncoding:
+ output = output.decode(outputEncoding)
+ assert output==expectedOutput, self._outputMismatchReport(output, expectedOutput)
+ except:
+ #print >>sys.stderr, moduleCode
+ raise
+ finally:
+ templateObj.shutdown()
+
+ def _getCompilerSettings(self):
+ return {}
+
+ def _outputMismatchReport(self, output, expectedOutput):
+ if self._debugEOLReplacement and self._EOLreplacement:
+ EOLrepl = self._EOLreplacement
+ marker = '*EOL*'
+ return self.report % {'template': self._input.replace(EOLrepl,marker),
+ 'expected': expectedOutput.replace(EOLrepl,marker),
+ 'actual': output.replace(EOLrepl,marker),
+ 'end': '(end)'}
+ else:
+ return self.report % {'template': self._input,
+ 'expected': expectedOutput,
+ 'actual': output,
+ 'end': '(end)'}
+
+ def genClassCode(self):
+ if hasattr(self, 'template'):
+ return self.template.generatedClassCode()
+
+ def genModuleCode(self):
+ if hasattr(self, 'template'):
+ return self.template.generatedModuleCode()
+
+##################################################
+## TEST CASE CLASSES
+
+class EmptyTemplate(OutputTest):
+ convertEOLs = False
+ def test1(self):
+ """an empty string for the template"""
+
+ warnings.filterwarnings('error',
+ 'You supplied an empty string for the source!',
+ UserWarning)
+ try:
+ self.verify("", "")
+ except UserWarning:
+ pass
+ else:
+ self.fail("Should warn about empty source strings.")
+
+ try:
+ self.verify("#implements foo", "")
+ except NotImplementedError:
+ pass
+ else:
+ self.fail("This should barf about respond() not being implemented.")
+
+ self.verify("#implements respond", "")
+
+ self.verify("#implements respond(foo=1234)", "")
+
+
+class Backslashes(OutputTest):
+ convertEOLs = False
+
+ def setUp(self):
+ fp = open('backslashes.txt','w')
+ fp.write(r'\ #LogFormat "%h %l %u %t \"%r\" %>s %b"' + '\n\n\n\n\n\n\n')
+ fp.flush()
+ fp.close
+
+ def tearDown(self):
+ if os.path.exists('backslashes.txt'):
+ os.remove('backslashes.txt')
+
+ def test1(self):
+ """ a single \\ using rawstrings"""
+ self.verify(r"\ ",
+ r"\ ")
+
+ def test2(self):
+ """ a single \\ using rawstrings and lots of lines"""
+ self.verify(r"\ " + "\n\n\n\n\n\n\n\n\n",
+ r"\ " + "\n\n\n\n\n\n\n\n\n")
+
+ def test3(self):
+ """ a single \\ without using rawstrings"""
+ self.verify("\ \ ",
+ "\ \ ")
+
+ def test4(self):
+ """ single line from an apache conf file"""
+ self.verify(r'#LogFormat "%h %l %u %t \"%r\" %>s %b"',
+ r'#LogFormat "%h %l %u %t \"%r\" %>s %b"')
+
+ def test5(self):
+ """ single line from an apache conf file with many NEWLINES
+
+ The NEWLINES are used to make sure that MethodCompiler.commitStrConst()
+ is handling long and short strings in the same fashion. It uses
+ triple-quotes for strings with lots of \\n in them and repr(theStr) for
+ shorter strings with only a few newlines."""
+
+ self.verify(r'#LogFormat "%h %l %u %t \"%r\" %>s %b"' + '\n\n\n\n\n\n\n',
+ r'#LogFormat "%h %l %u %t \"%r\" %>s %b"' + '\n\n\n\n\n\n\n')
+
+ def test6(self):
+ """ test backslash handling in an included file"""
+ self.verify(r'#include "backslashes.txt"',
+ r'\ #LogFormat "%h %l %u %t \"%r\" %>s %b"' + '\n\n\n\n\n\n\n')
+
+ def test7(self):
+ """ a single \\ without using rawstrings plus many NEWLINES"""
+ self.verify("\ \ " + "\n\n\n\n\n\n\n\n\n",
+ "\ \ " + "\n\n\n\n\n\n\n\n\n")
+
+ def test8(self):
+ """ single line from an apache conf file with single quotes and many NEWLINES
+ """
+
+ self.verify(r"""#LogFormat '%h %l %u %t \"%r\" %>s %b'""" + '\n\n\n\n\n\n\n',
+ r"""#LogFormat '%h %l %u %t \"%r\" %>s %b'""" + '\n\n\n\n\n\n\n')
+
+class NonTokens(OutputTest):
+ def test1(self):
+ """dollar signs not in Cheetah $vars"""
+ self.verify("$ $$ $5 $. $ test",
+ "$ $$ $5 $. $ test")
+
+ def test2(self):
+ """hash not in #directives"""
+ self.verify("# \# #5 ",
+ "# # #5 ")
+
+ def test3(self):
+ """escapted comments"""
+ self.verify(" \##escaped comment ",
+ " ##escaped comment ")
+
+ def test4(self):
+ """escapted multi-line comments"""
+ self.verify(" \#*escaped comment \n*# ",
+ " #*escaped comment \n*# ")
+
+ def test5(self):
+ """1 dollar sign"""
+ self.verify("$",
+ "$")
+ def _X_test6(self):
+ """1 dollar sign followed by hash"""
+ self.verify("\n$#\n",
+ "\n$#\n")
+
+ def test6(self):
+ """1 dollar sign followed by EOL Slurp Token"""
+ if DEFAULT_COMPILER_SETTINGS['EOLSlurpToken']:
+ self.verify("\n$%s\n"%DEFAULT_COMPILER_SETTINGS['EOLSlurpToken'],
+ "\n$")
+ else:
+ self.verify("\n$#\n",
+ "\n$#\n")
+
+class Comments_SingleLine(OutputTest):
+ def test1(self):
+ """## followed by WS"""
+ self.verify("## ",
+ "")
+
+ def test2(self):
+ """## followed by NEWLINE"""
+ self.verify("##\n",
+ "")
+
+ def test3(self):
+ """## followed by text then NEWLINE"""
+ self.verify("## oeuao aoe uaoe \n",
+ "")
+ def test4(self):
+ """## gobbles leading WS"""
+ self.verify(" ## oeuao aoe uaoe \n",
+ "")
+
+ def test5(self):
+ """## followed by text then NEWLINE, + leading WS"""
+ self.verify(" ## oeuao aoe uaoe \n",
+ "")
+
+ def test6(self):
+ """## followed by EOF"""
+ self.verify("##",
+ "")
+
+ def test7(self):
+ """## followed by EOF with leading WS"""
+ self.verify(" ##",
+ "")
+
+ def test8(self):
+ """## gobble line
+ with text on previous and following lines"""
+ self.verify("line1\n ## aoeu 1234 \nline2",
+ "line1\nline2")
+
+ def test9(self):
+ """## don't gobble line
+ with text on previous and following lines"""
+ self.verify("line1\n 12 ## aoeu 1234 \nline2",
+ "line1\n 12 \nline2")
+
+ def test10(self):
+ """## containing $placeholders
+ """
+ self.verify("##$a$b $c($d)",
+ "")
+
+ def test11(self):
+ """## containing #for directive
+ """
+ self.verify("##for $i in range(15)",
+ "")
+
+
+class Comments_MultiLine_NoGobble(OutputTest):
+ """
+ Multiline comments used to not gobble whitespace. They do now, but this can
+ be turned off with a compilerSetting
+ """
+
+ def _getCompilerSettings(self):
+ return {'gobbleWhitespaceAroundMultiLineComments':False}
+
+ def test1(self):
+ """#* *# followed by WS
+ Shouldn't gobble WS
+ """
+ self.verify("#* blarg *# ",
+ " ")
+
+ def test2(self):
+ """#* *# preceded and followed by WS
+ Shouldn't gobble WS
+ """
+ self.verify(" #* blarg *# ",
+ " ")
+
+ def test3(self):
+ """#* *# followed by WS, with NEWLINE
+ Shouldn't gobble WS
+ """
+ self.verify("#* \nblarg\n *# ",
+ " ")
+
+ def test4(self):
+ """#* *# preceded and followed by WS, with NEWLINE
+ Shouldn't gobble WS
+ """
+ self.verify(" #* \nblarg\n *# ",
+ " ")
+
+class Comments_MultiLine(OutputTest):
+ """
+ Note: Multiline comments don't gobble whitespace!
+ """
+
+ def test1(self):
+ """#* *# followed by WS
+ Should gobble WS
+ """
+ self.verify("#* blarg *# ",
+ "")
+
+ def test2(self):
+ """#* *# preceded and followed by WS
+ Should gobble WS
+ """
+ self.verify(" #* blarg *# ",
+ "")
+
+ def test3(self):
+ """#* *# followed by WS, with NEWLINE
+ Shouldn't gobble WS
+ """
+ self.verify("#* \nblarg\n *# ",
+ "")
+
+ def test4(self):
+ """#* *# preceded and followed by WS, with NEWLINE
+ Shouldn't gobble WS
+ """
+ self.verify(" #* \nblarg\n *# ",
+ "")
+
+ def test5(self):
+ """#* *# containing nothing
+ """
+ self.verify("#**#",
+ "")
+
+ def test6(self):
+ """#* *# containing only NEWLINES
+ """
+ self.verify(" #*\n\n\n\n\n\n\n\n*# ",
+ "")
+
+ def test7(self):
+ """#* *# containing $placeholders
+ """
+ self.verify("#* $var $var(1234*$c) *#",
+ "")
+
+ def test8(self):
+ """#* *# containing #for directive
+ """
+ self.verify("#* #for $i in range(15) *#",
+ "")
+
+ def test9(self):
+ """ text around #* *# containing #for directive
+ """
+ self.verify("foo\nfoo bar #* #for $i in range(15) *# foo\n",
+ "foo\nfoo bar foo\n")
+
+ def test9(self):
+ """ text around #* *# containing #for directive and trailing whitespace
+ which should be gobbled
+ """
+ self.verify("foo\nfoo bar #* #for $i in range(15) *# \ntest",
+ "foo\nfoo bar \ntest")
+
+ def test10(self):
+ """ text around #* *# containing #for directive and newlines: trailing whitespace
+ which should be gobbled.
+ """
+ self.verify("foo\nfoo bar #* \n\n#for $i in range(15) \n\n*# \ntest",
+ "foo\nfoo bar \ntest")
+
+class Placeholders(OutputTest):
+ def test1(self):
+ """1 placeholder"""
+ self.verify("$aStr", "blarg")
+
+ def test2(self):
+ """2 placeholders"""
+ self.verify("$aStr $anInt", "blarg 1")
+
+ def test3(self):
+ """2 placeholders, back-to-back"""
+ self.verify("$aStr$anInt", "blarg1")
+
+ def test4(self):
+ """1 placeholder enclosed in ()"""
+ self.verify("$(aStr)", "blarg")
+
+ def test5(self):
+ """1 placeholder enclosed in {}"""
+ self.verify("${aStr}", "blarg")
+
+ def test6(self):
+ """1 placeholder enclosed in []"""
+ self.verify("$[aStr]", "blarg")
+
+ def test7(self):
+ """1 placeholder enclosed in () + WS
+
+ Test to make sure that $(<WS><identifier>.. matches
+ """
+ self.verify("$( aStr )", "blarg")
+
+ def test8(self):
+ """1 placeholder enclosed in {} + WS"""
+ self.verify("${ aStr }", "blarg")
+
+ def test9(self):
+ """1 placeholder enclosed in [] + WS"""
+ self.verify("$[ aStr ]", "blarg")
+
+ def test10(self):
+ """1 placeholder enclosed in () + WS + * cache
+
+ Test to make sure that $*(<WS><identifier>.. matches
+ """
+ self.verify("$*( aStr )", "blarg")
+
+ def test11(self):
+ """1 placeholder enclosed in {} + WS + *cache"""
+ self.verify("$*{ aStr }", "blarg")
+
+ def test12(self):
+ """1 placeholder enclosed in [] + WS + *cache"""
+ self.verify("$*[ aStr ]", "blarg")
+
+ def test13(self):
+ """1 placeholder enclosed in {} + WS + *<int>*cache"""
+ self.verify("$*5*{ aStr }", "blarg")
+
+ def test14(self):
+ """1 placeholder enclosed in [] + WS + *<int>*cache"""
+ self.verify("$*5*[ aStr ]", "blarg")
+
+ def test15(self):
+ """1 placeholder enclosed in {} + WS + *<float>*cache"""
+ self.verify("$*0.5d*{ aStr }", "blarg")
+
+ def test16(self):
+ """1 placeholder enclosed in [] + WS + *<float>*cache"""
+ self.verify("$*.5*[ aStr ]", "blarg")
+
+ def test17(self):
+ """1 placeholder + *<int>*cache"""
+ self.verify("$*5*aStr", "blarg")
+
+ def test18(self):
+ """1 placeholder *<float>*cache"""
+ self.verify("$*0.5h*aStr", "blarg")
+
+ def test19(self):
+ """1 placeholder surrounded by single quotes and multiple newlines"""
+ self.verify("""'\n\n\n\n'$aStr'\n\n\n\n'""",
+ """'\n\n\n\n'blarg'\n\n\n\n'""")
+
+ def test20(self):
+ """silent mode $!placeholders """
+ self.verify("$!aStr$!nonExistant$!*nonExistant$!{nonExistant}", "blarg")
+
+ try:
+ self.verify("$!aStr$nonExistant",
+ "blarg")
+ except NotFound:
+ pass
+ else:
+ self.fail('should raise NotFound exception')
+
+ def test21(self):
+ """Make sure that $*caching is actually working"""
+ namesStr = 'You Me Them Everyone'
+ names = namesStr.split()
+
+ tmpl = Template.compile('#for name in $names: $name ', baseclass=dict)
+ assert str(tmpl({'names':names})).strip()==namesStr
+
+ tmpl = tmpl.subclass('#for name in $names: $*name ')
+ assert str(tmpl({'names':names}))=='You '*len(names)
+
+ tmpl = tmpl.subclass('#for name in $names: $*1*name ')
+ assert str(tmpl({'names':names}))=='You '*len(names)
+
+ tmpl = tmpl.subclass('#for name in $names: $*1*(name) ')
+ assert str(tmpl({'names':names}))=='You '*len(names)
+
+ if versionTuple > (2,2):
+ tmpl = tmpl.subclass('#for name in $names: $*1*(name) ')
+ assert str(tmpl(names=names))=='You '*len(names)
+
+class Placeholders_Vals(OutputTest):
+ convertEOLs = False
+ def test1(self):
+ """string"""
+ self.verify("$aStr", "blarg")
+
+ def test2(self):
+ """string - with whitespace"""
+ self.verify(" $aStr ", " blarg ")
+
+ def test3(self):
+ """empty string - with whitespace"""
+ self.verify("$emptyString", "")
+
+ def test4(self):
+ """int"""
+ self.verify("$anInt", "1")
+
+ def test5(self):
+ """float"""
+ self.verify("$aFloat", "1.5")
+
+ def test6(self):
+ """list"""
+ self.verify("$aList", "['item0', 'item1', 'item2']")
+
+ def test7(self):
+ """None
+
+ The default output filter is ReplaceNone.
+ """
+ self.verify("$none", "")
+
+ def test8(self):
+ """True, False
+ """
+ self.verify("$True $False", "%s %s"%(repr(True), repr(False)))
+
+ def test9(self):
+ """$_
+ """
+ self.verify("$_('foo')", "Translated: foo")
+
+class PlaceholderStrings(OutputTest):
+ def test1(self):
+ """some c'text $placeholder text' strings"""
+ self.verify("$str(c'$aStr')", "blarg")
+
+ def test2(self):
+ """some c'text $placeholder text' strings"""
+ self.verify("$str(c'$aStr.upper')", "BLARG")
+
+ def test3(self):
+ """some c'text $placeholder text' strings"""
+ self.verify("$str(c'$(aStr.upper.replace(c\"A$str()\",\"\"))')", "BLRG")
+
+ def test4(self):
+ """some c'text $placeholder text' strings"""
+ self.verify("#echo $str(c'$(aStr.upper)')", "BLARG")
+
+ def test5(self):
+ """some c'text $placeholder text' strings"""
+ self.verify("#if 1 then $str(c'$(aStr.upper)') else 0", "BLARG")
+
+ def test6(self):
+ """some c'text $placeholder text' strings"""
+ self.verify("#if 1\n$str(c'$(aStr.upper)')#slurp\n#else\n0#end if", "BLARG")
+
+ def test7(self):
+ """some c'text $placeholder text' strings"""
+ self.verify("#def foo(arg=c'$(\"BLARG\")')\n"
+ "$arg#slurp\n"
+ "#end def\n"
+ "$foo()$foo(c'$anInt')#slurp",
+
+ "BLARG1")
+
+
+
+class UnicodeStrings(OutputTest):
+ def test1(self):
+ """unicode data in placeholder
+ """
+ #self.verify(u"$unicodeData", defaultTestNameSpace['unicodeData'], outputEncoding='utf8')
+ self.verify(u"$unicodeData", defaultTestNameSpace['unicodeData'])
+
+ def test2(self):
+ """unicode data in body
+ """
+ self.verify(u"aoeu12345\u1234", u"aoeu12345\u1234")
+ #self.verify(u"#encoding utf8#aoeu12345\u1234", u"aoeu12345\u1234")
+
+class EncodingDirective(OutputTest):
+ def test1(self):
+ """basic #encoding """
+ self.verify("#encoding utf-8\n1234",
+ "1234")
+
+ def test2(self):
+ """basic #encoding """
+ self.verify("#encoding ascii\n1234",
+ "1234")
+
+ def test3(self):
+ """basic #encoding """
+ self.verify("#encoding utf-8\n\xe1\x88\xb4",
+ u'\u1234', outputEncoding='utf8')
+
+ def test4(self):
+ """basic #encoding """
+ self.verify("#encoding ascii\n\xe1\x88\xb4",
+ "\xe1\x88\xb4")
+
+ def test5(self):
+ """basic #encoding """
+ self.verify("#encoding latin-1\nAndr\202",
+ u'Andr\202', outputEncoding='latin-1')
+
+class Placeholders_Esc(OutputTest):
+ convertEOLs = False
+ def test1(self):
+ """1 escaped placeholder"""
+ self.verify("\$var",
+ "$var")
+
+ def test2(self):
+ """2 escaped placeholders"""
+ self.verify("\$var \$_",
+ "$var $_")
+
+ def test3(self):
+ """2 escaped placeholders - back to back"""
+ self.verify("\$var\$_",
+ "$var$_")
+
+ def test4(self):
+ """2 escaped placeholders - nested"""
+ self.verify("\$var(\$_)",
+ "$var($_)")
+
+ def test5(self):
+ """2 escaped placeholders - nested and enclosed"""
+ self.verify("\$(var(\$_)",
+ "$(var($_)")
+
+
+class Placeholders_Calls(OutputTest):
+ def test1(self):
+ """func placeholder - no ()"""
+ self.verify("$aFunc",
+ "Scooby")
+
+ def test2(self):
+ """func placeholder - with ()"""
+ self.verify("$aFunc()",
+ "Scooby")
+
+ def test3(self):
+ r"""func placeholder - with (\n\n)"""
+ self.verify("$aFunc(\n\n)",
+ "Scooby", convertEOLs=False)
+
+ def test4(self):
+ r"""func placeholder - with (\n\n) and $() enclosure"""
+ self.verify("$(aFunc(\n\n))",
+ "Scooby", convertEOLs=False)
+
+ def test5(self):
+ r"""func placeholder - with (\n\n) and ${} enclosure"""
+ self.verify("${aFunc(\n\n)}",
+ "Scooby", convertEOLs=False)
+
+ def test6(self):
+ """func placeholder - with (int)"""
+ self.verify("$aFunc(1234)",
+ "1234")
+
+ def test7(self):
+ r"""func placeholder - with (\nint\n)"""
+ self.verify("$aFunc(\n1234\n)",
+ "1234", convertEOLs=False)
+ def test8(self):
+ """func placeholder - with (string)"""
+ self.verify("$aFunc('aoeu')",
+ "aoeu")
+
+ def test9(self):
+ """func placeholder - with ('''string''')"""
+ self.verify("$aFunc('''aoeu''')",
+ "aoeu")
+ def test10(self):
+ r"""func placeholder - with ('''\nstring\n''')"""
+ self.verify("$aFunc('''\naoeu\n''')",
+ "\naoeu\n")
+
+ def test11(self):
+ r"""func placeholder - with ('''\nstring'\n''')"""
+ self.verify("$aFunc('''\naoeu'\n''')",
+ "\naoeu'\n")
+
+ def test12(self):
+ r'''func placeholder - with ("""\nstring\n""")'''
+ self.verify('$aFunc("""\naoeu\n""")',
+ "\naoeu\n")
+
+ def test13(self):
+ """func placeholder - with (string*int)"""
+ self.verify("$aFunc('aoeu'*2)",
+ "aoeuaoeu")
+
+ def test14(self):
+ """func placeholder - with (int*int)"""
+ self.verify("$aFunc(2*2)",
+ "4")
+
+ def test15(self):
+ """func placeholder - with (int*float)"""
+ self.verify("$aFunc(2*2.0)",
+ "4.0")
+
+ def test16(self):
+ r"""func placeholder - with (int\n*\nfloat)"""
+ self.verify("$aFunc(2\n*\n2.0)",
+ "4.0", convertEOLs=False)
+
+ def test17(self):
+ """func placeholder - with ($arg=float)"""
+ self.verify("$aFunc($arg=4.0)",
+ "4.0")
+
+ def test18(self):
+ """func placeholder - with (arg=float)"""
+ self.verify("$aFunc(arg=4.0)",
+ "4.0")
+
+ def test19(self):
+ """deeply nested argstring, no enclosure"""
+ self.verify("$aFunc($arg=$aMeth($arg=$aFunc(1)))",
+ "1")
+
+ def test20(self):
+ """deeply nested argstring, no enclosure + with WS"""
+ self.verify("$aFunc( $arg = $aMeth( $arg = $aFunc( 1 ) ) )",
+ "1")
+ def test21(self):
+ """deeply nested argstring, () enclosure + with WS"""
+ self.verify("$(aFunc( $arg = $aMeth( $arg = $aFunc( 1 ) ) ) )",
+ "1")
+
+ def test22(self):
+ """deeply nested argstring, {} enclosure + with WS"""
+ self.verify("${aFunc( $arg = $aMeth( $arg = $aFunc( 1 ) ) ) }",
+ "1")
+
+ def test23(self):
+ """deeply nested argstring, [] enclosure + with WS"""
+ self.verify("$[aFunc( $arg = $aMeth( $arg = $aFunc( 1 ) ) ) ]",
+ "1")
+
+ def test24(self):
+ """deeply nested argstring, () enclosure + *cache"""
+ self.verify("$*(aFunc( $arg = $aMeth( $arg = $aFunc( 1 ) ) ) )",
+ "1")
+ def test25(self):
+ """deeply nested argstring, () enclosure + *15*cache"""
+ self.verify("$*15*(aFunc( $arg = $aMeth( $arg = $aFunc( 1 ) ) ) )",
+ "1")
+
+ def test26(self):
+ """a function call with the Python None kw."""
+ self.verify("$aFunc(None)",
+ "")
+
+class NameMapper(OutputTest):
+ def test1(self):
+ """autocalling"""
+ self.verify("$aFunc! $aFunc().",
+ "Scooby! Scooby.")
+
+ def test2(self):
+ """nested autocalling"""
+ self.verify("$aFunc($aFunc).",
+ "Scooby.")
+
+ def test3(self):
+ """list subscription"""
+ self.verify("$aList[0]",
+ "item0")
+
+ def test4(self):
+ """list slicing"""
+ self.verify("$aList[:2]",
+ "['item0', 'item1']")
+
+ def test5(self):
+ """list slicing and subcription combined"""
+ self.verify("$aList[:2][0]",
+ "item0")
+
+ def test6(self):
+ """dictionary access - NameMapper style"""
+ self.verify("$aDict.one",
+ "item1")
+
+ def test7(self):
+ """dictionary access - Python style"""
+ self.verify("$aDict['one']",
+ "item1")
+
+ def test8(self):
+ """dictionary access combined with autocalled string method"""
+ self.verify("$aDict.one.upper",
+ "ITEM1")
+
+ def test9(self):
+ """dictionary access combined with string method"""
+ self.verify("$aDict.one.upper()",
+ "ITEM1")
+
+ def test10(self):
+ """nested dictionary access - NameMapper style"""
+ self.verify("$aDict.nestedDict.two",
+ "nestedItem2")
+
+ def test11(self):
+ """nested dictionary access - Python style"""
+ self.verify("$aDict['nestedDict']['two']",
+ "nestedItem2")
+
+ def test12(self):
+ """nested dictionary access - alternating style"""
+ self.verify("$aDict['nestedDict'].two",
+ "nestedItem2")
+
+ def test13(self):
+ """nested dictionary access using method - alternating style"""
+ self.verify("$aDict.get('nestedDict').two",
+ "nestedItem2")
+
+ def test14(self):
+ """nested dictionary access - NameMapper style - followed by method"""
+ self.verify("$aDict.nestedDict.two.upper",
+ "NESTEDITEM2")
+
+ def test15(self):
+ """nested dictionary access - alternating style - followed by method"""
+ self.verify("$aDict['nestedDict'].two.upper",
+ "NESTEDITEM2")
+
+ def test16(self):
+ """nested dictionary access - NameMapper style - followed by method, then slice"""
+ self.verify("$aDict.nestedDict.two.upper[:4]",
+ "NEST")
+
+ def test17(self):
+ """nested dictionary access - Python style using a soft-coded key"""
+ self.verify("$aDict[$anObj.meth('nestedDict')].two",
+ "nestedItem2")
+
+ def test18(self):
+ """object method access"""
+ self.verify("$anObj.meth1",
+ "doo")
+
+ def test19(self):
+ """object method access, followed by complex slice"""
+ self.verify("$anObj.meth1[0: ((4/4*2)*2)/$anObj.meth1(2) ]",
+ "do")
+
+ def test20(self):
+ """object method access, followed by a very complex slice
+ If it can pass this one, it's safe to say it works!!"""
+ self.verify("$( anObj.meth1[0:\n (\n(4/4*2)*2)/$anObj.meth1(2)\n ] )",
+ "do")
+
+ def test21(self):
+ """object method access with % in the default arg for the meth.
+
+ This tests a bug that Jeff Johnson found and submitted a patch to SF
+ for."""
+
+ self.verify("$anObj.methWithPercentSignDefaultArg",
+ "110%")
+
+
+#class NameMapperDict(OutputTest):
+#
+# _searchList = [{"update": "Yabba dabba doo!"}]
+#
+# def test1(self):
+# if NameMapper_C_VERSION:
+# return # This feature is not in the C version yet.
+# self.verify("$update", "Yabba dabba doo!")
+#
+
+class CacheDirective(OutputTest):
+
+ def test1(self):
+ r"""simple #cache """
+ self.verify("#cache:$anInt",
+ "1")
+
+ def test2(self):
+ r"""simple #cache + WS"""
+ self.verify(" #cache \n$anInt#end cache",
+ "1")
+
+ def test3(self):
+ r"""simple #cache ... #end cache"""
+ self.verify("""#cache id='cache1', timer=150m
+$anInt
+#end cache
+$aStr""",
+ "1\nblarg")
+
+ def test4(self):
+ r"""2 #cache ... #end cache blocks"""
+ self.verify("""#slurp
+#def foo
+#cache ID='cache1', timer=150m
+$anInt
+#end cache
+#cache id='cache2', timer=15s
+ #for $i in range(5)
+$i#slurp
+ #end for
+#end cache
+$aStr#slurp
+#end def
+$foo$foo$foo$foo$foo""",
+ "1\n01234blarg"*5)
+
+
+ def test5(self):
+ r"""nested #cache blocks"""
+ self.verify("""#slurp
+#def foo
+#cache ID='cache1', timer=150m
+$anInt
+#cache id='cache2', timer=15s
+ #for $i in range(5)
+$i#slurp
+ #end for
+$*(6)#slurp
+#end cache
+#end cache
+$aStr#slurp
+#end def
+$foo$foo$foo$foo$foo""",
+ "1\n012346blarg"*5)
+
+
+class CallDirective(OutputTest):
+
+ def test1(self):
+ r"""simple #call """
+ self.verify("#call int\n$anInt#end call",
+ "1")
+ # single line version
+ self.verify("#call int: $anInt",
+ "1")
+ self.verify("#call int: 10\n$aStr",
+ "10\nblarg")
+
+ def test2(self):
+ r"""simple #call + WS"""
+ self.verify("#call int\n$anInt #end call",
+ "1")
+
+ def test3(self):
+ r"""a longer #call"""
+ self.verify('''\
+#def meth(arg)
+$arg.upper()#slurp
+#end def
+#call $meth
+$(1234+1) foo#slurp
+#end call''',
+ "1235 FOO")
+
+ def test4(self):
+ r"""#call with keyword #args"""
+ self.verify('''\
+#def meth(arg1, arg2)
+$arg1.upper() - $arg2.lower()#slurp
+#end def
+#call self.meth
+#arg arg1
+$(1234+1) foo#slurp
+#arg arg2
+UPPER#slurp
+#end call''',
+ "1235 FOO - upper")
+
+ def test5(self):
+ r"""#call with single-line keyword #args """
+ self.verify('''\
+#def meth(arg1, arg2)
+$arg1.upper() - $arg2.lower()#slurp
+#end def
+#call self.meth
+#arg arg1:$(1234+1) foo#slurp
+#arg arg2:UPPER#slurp
+#end call''',
+ "1235 FOO - upper")
+
+ def test6(self):
+ """#call with python kwargs and cheetah output for the 1s positional
+ arg"""
+
+ self.verify('''\
+#def meth(arg1, arg2)
+$arg1.upper() - $arg2.lower()#slurp
+#end def
+#call self.meth arg2="UPPER"
+$(1234+1) foo#slurp
+#end call''',
+ "1235 FOO - upper")
+
+ def test7(self):
+ """#call with python kwargs and #args"""
+ self.verify('''\
+#def meth(arg1, arg2, arg3)
+$arg1.upper() - $arg2.lower() - $arg3#slurp
+#end def
+#call self.meth arg2="UPPER", arg3=999
+#arg arg1:$(1234+1) foo#slurp
+#end call''',
+ "1235 FOO - upper - 999")
+
+ def test8(self):
+ """#call with python kwargs and #args, and using a function to get the
+ function that will be called"""
+ self.verify('''\
+#def meth(arg1, arg2, arg3)
+$arg1.upper() - $arg2.lower() - $arg3#slurp
+#end def
+#call getattr(self, "meth") arg2="UPPER", arg3=999
+#arg arg1:$(1234+1) foo#slurp
+#end call''',
+ "1235 FOO - upper - 999")
+
+ def test9(self):
+ """nested #call directives"""
+ self.verify('''\
+#def meth(arg1)
+$arg1#slurp
+#end def
+#def meth2(x,y)
+$x$y#slurp
+#end def
+##
+#call self.meth
+1#slurp
+#call self.meth
+2#slurp
+#call self.meth
+3#slurp
+#end call 3
+#set two = 2
+#call self.meth2 y=c"$(10/$two)"
+#arg x
+4#slurp
+#end call 4
+#end call 2
+#end call 1''',
+ "12345")
+
+
+
+class I18nDirective(OutputTest):
+ def test1(self):
+ r"""simple #call """
+ self.verify("#i18n \n$anInt#end i18n",
+ "1")
+
+ # single line version
+ self.verify("#i18n: $anInt",
+ "1")
+ self.verify("#i18n: 10\n$aStr",
+ "10\nblarg")
+
+
+class CaptureDirective(OutputTest):
+ def test1(self):
+ r"""simple #capture"""
+ self.verify('''\
+#capture cap1
+$(1234+1) foo#slurp
+#end capture
+$cap1#slurp
+''',
+ "1235 foo")
+
+
+ def test2(self):
+ r"""slightly more complex #capture"""
+ self.verify('''\
+#def meth(arg)
+$arg.upper()#slurp
+#end def
+#capture cap1
+$(1234+1) $anInt $meth("foo")#slurp
+#end capture
+$cap1#slurp
+''',
+ "1235 1 FOO")
+
+
+class SlurpDirective(OutputTest):
+ def test1(self):
+ r"""#slurp with 1 \n """
+ self.verify("#slurp\n",
+ "")
+
+ def test2(self):
+ r"""#slurp with 1 \n, leading whitespace
+ Should gobble"""
+ self.verify(" #slurp\n",
+ "")
+
+ def test3(self):
+ r"""#slurp with 1 \n, leading content
+ Shouldn't gobble"""
+ self.verify(" 1234 #slurp\n",
+ " 1234 ")
+
+ def test4(self):
+ r"""#slurp with WS then \n, leading content
+ Shouldn't gobble"""
+ self.verify(" 1234 #slurp \n",
+ " 1234 ")
+
+ def test5(self):
+ r"""#slurp with garbage chars then \n, leading content
+ Should eat the garbage"""
+ self.verify(" 1234 #slurp garbage \n",
+ " 1234 ")
+
+
+
+class EOLSlurpToken(OutputTest):
+ _EOLSlurpToken = DEFAULT_COMPILER_SETTINGS['EOLSlurpToken']
+ def test1(self):
+ r"""#slurp with 1 \n """
+ self.verify("%s\n"%self._EOLSlurpToken,
+ "")
+
+ def test2(self):
+ r"""#slurp with 1 \n, leading whitespace
+ Should gobble"""
+ self.verify(" %s\n"%self._EOLSlurpToken,
+ "")
+ def test3(self):
+ r"""#slurp with 1 \n, leading content
+ Shouldn't gobble"""
+ self.verify(" 1234 %s\n"%self._EOLSlurpToken,
+ " 1234 ")
+
+ def test4(self):
+ r"""#slurp with WS then \n, leading content
+ Shouldn't gobble"""
+ self.verify(" 1234 %s \n"%self._EOLSlurpToken,
+ " 1234 ")
+
+ def test5(self):
+ r"""#slurp with garbage chars then \n, leading content
+ Should NOT eat the garbage"""
+ self.verify(" 1234 %s garbage \n"%self._EOLSlurpToken,
+ " 1234 %s garbage \n"%self._EOLSlurpToken)
+
+if not DEFAULT_COMPILER_SETTINGS['EOLSlurpToken']:
+ del EOLSlurpToken
+
+class RawDirective(OutputTest):
+ def test1(self):
+ """#raw till EOF"""
+ self.verify("#raw\n$aFunc().\n\n",
+ "$aFunc().\n\n")
+
+ def test2(self):
+ """#raw till #end raw"""
+ self.verify("#raw\n$aFunc().\n#end raw\n$anInt",
+ "$aFunc().\n1")
+
+ def test3(self):
+ """#raw till #end raw gobble WS"""
+ self.verify(" #raw \n$aFunc().\n #end raw \n$anInt",
+ "$aFunc().\n1")
+
+ def test4(self):
+ """#raw till #end raw using explicit directive closure
+ Shouldn't gobble"""
+ self.verify(" #raw #\n$aFunc().\n #end raw #\n$anInt",
+ " \n$aFunc().\n\n1")
+
+ def test5(self):
+ """single-line short form #raw: """
+ self.verify("#raw: $aFunc().\n\n",
+ "$aFunc().\n\n")
+
+ self.verify("#raw: $aFunc().\n$anInt",
+ "$aFunc().\n1")
+
+class BreakpointDirective(OutputTest):
+ def test1(self):
+ """#breakpoint part way through source code"""
+ self.verify("$aFunc(2).\n#breakpoint\n$anInt",
+ "2.\n")
+
+ def test2(self):
+ """#breakpoint at BOF"""
+ self.verify("#breakpoint\n$anInt",
+ "")
+
+ def test3(self):
+ """#breakpoint at EOF"""
+ self.verify("$anInt\n#breakpoint",
+ "1\n")
+
+
+class StopDirective(OutputTest):
+ def test1(self):
+ """#stop part way through source code"""
+ self.verify("$aFunc(2).\n#stop\n$anInt",
+ "2.\n")
+
+ def test2(self):
+ """#stop at BOF"""
+ self.verify("#stop\n$anInt",
+ "")
+
+ def test3(self):
+ """#stop at EOF"""
+ self.verify("$anInt\n#stop",
+ "1\n")
+
+ def test4(self):
+ """#stop in pos test block"""
+ self.verify("""$anInt
+#if 1
+inside the if block
+#stop
+#end if
+blarg""",
+ "1\ninside the if block\n")
+
+ def test5(self):
+ """#stop in neg test block"""
+ self.verify("""$anInt
+#if 0
+inside the if block
+#stop
+#end if
+blarg""",
+ "1\nblarg")
+
+
+class ReturnDirective(OutputTest):
+
+ def test1(self):
+ """#return'ing an int """
+ self.verify("""1
+$str($test-6)
+3
+#def test
+#if 1
+#return (3 *2) \
+ + 2
+#else
+aoeuoaeu
+#end if
+#end def
+""",
+ "1\n2\n3\n")
+
+ def test2(self):
+ """#return'ing an string """
+ self.verify("""1
+$str($test[1])
+3
+#def test
+#if 1
+#return '123'
+#else
+aoeuoaeu
+#end if
+#end def
+""",
+ "1\n2\n3\n")
+
+ def test3(self):
+ """#return'ing an string AND streaming other output via the transaction"""
+ self.verify("""1
+$str($test(trans=trans)[1])
+3
+#def test
+1.5
+#if 1
+#return '123'
+#else
+aoeuoaeu
+#end if
+#end def
+""",
+ "1\n1.5\n2\n3\n")
+
+
+class YieldDirective(OutputTest):
+ convertEOLs = False
+ def test1(self):
+ """simple #yield """
+
+ src1 = """#for i in range(10)\n#yield i\n#end for"""
+ src2 = """#for i in range(10)\n$i#slurp\n#yield\n#end for"""
+ src3 = ("#def iterator\n"
+ "#for i in range(10)\n#yield i\n#end for\n"
+ "#end def\n"
+ "#for i in $iterator\n$i#end for"
+ )
+
+
+ for src in (src1,src2,src3):
+ klass = Template.compile(src, keepRefToGeneratedCode=True)
+ #print klass._CHEETAH_generatedModuleCode
+ iter = klass().respond()
+ output = [str(i) for i in iter]
+ assert ''.join(output)=='0123456789'
+ #print ''.join(output)
+
+ # @@TR: need to expand this to cover error conditions etc.
+
+if versionTuple < (2,3):
+ del YieldDirective
+
+class ForDirective(OutputTest):
+
+ def test1(self):
+ """#for loop with one local var"""
+ self.verify("#for $i in range(5)\n$i\n#end for",
+ "0\n1\n2\n3\n4\n")
+
+ self.verify("#for $i in range(5):\n$i\n#end for",
+ "0\n1\n2\n3\n4\n")
+
+ self.verify("#for $i in range(5): ##comment\n$i\n#end for",
+ "0\n1\n2\n3\n4\n")
+
+ self.verify("#for $i in range(5) ##comment\n$i\n#end for",
+ "0\n1\n2\n3\n4\n")
+
+
+ def test2(self):
+ """#for loop with WS in loop"""
+ self.verify("#for $i in range(5)\n$i \n#end for",
+ "0 \n1 \n2 \n3 \n4 \n")
+
+ def test3(self):
+ """#for loop gobble WS"""
+ self.verify(" #for $i in range(5) \n$i \n #end for ",
+ "0 \n1 \n2 \n3 \n4 \n")
+
+ def test4(self):
+ """#for loop over list"""
+ self.verify("#for $i, $j in [(0,1),(2,3)]\n$i,$j\n#end for",
+ "0,1\n2,3\n")
+
+ def test5(self):
+ """#for loop over list, with #slurp"""
+ self.verify("#for $i, $j in [(0,1),(2,3)]\n$i,$j#slurp\n#end for",
+ "0,12,3")
+
+ def test6(self):
+ """#for loop with explicit closures"""
+ self.verify("#for $i in range(5)#$i#end for#",
+ "01234")
+
+ def test7(self):
+ """#for loop with explicit closures and WS"""
+ self.verify(" #for $i in range(5)#$i#end for# ",
+ " 01234 ")
+
+ def test8(self):
+ """#for loop using another $var"""
+ self.verify(" #for $i in range($aFunc(5))#$i#end for# ",
+ " 01234 ")
+
+ def test9(self):
+ """test methods in for loops"""
+ self.verify("#for $func in $listOfLambdas\n$func($anInt)\n#end for",
+ "1\n1\n1\n")
+
+
+ def test10(self):
+ """#for loop over list, using methods of the items"""
+ self.verify("#for i, j in [('aa','bb'),('cc','dd')]\n$i.upper,$j.upper\n#end for",
+ "AA,BB\nCC,DD\n")
+ self.verify("#for $i, $j in [('aa','bb'),('cc','dd')]\n$i.upper,$j.upper\n#end for",
+ "AA,BB\nCC,DD\n")
+
+ def test11(self):
+ """#for loop over list, using ($i,$j) style target list"""
+ self.verify("#for (i, j) in [('aa','bb'),('cc','dd')]\n$i.upper,$j.upper\n#end for",
+ "AA,BB\nCC,DD\n")
+ self.verify("#for ($i, $j) in [('aa','bb'),('cc','dd')]\n$i.upper,$j.upper\n#end for",
+ "AA,BB\nCC,DD\n")
+
+ def test12(self):
+ """#for loop over list, using i, (j,k) style target list"""
+ self.verify("#for i, (j, k) in enumerate([('aa','bb'),('cc','dd')])\n$j.upper,$k.upper\n#end for",
+ "AA,BB\nCC,DD\n")
+ self.verify("#for $i, ($j, $k) in enumerate([('aa','bb'),('cc','dd')])\n$j.upper,$k.upper\n#end for",
+ "AA,BB\nCC,DD\n")
+
+ def test13(self):
+ """single line #for"""
+ self.verify("#for $i in range($aFunc(5)): $i",
+ "01234")
+
+ def test14(self):
+ """single line #for with 1 extra leading space"""
+ self.verify("#for $i in range($aFunc(5)): $i",
+ " 0 1 2 3 4")
+
+ def test15(self):
+ """2 times single line #for"""
+ self.verify("#for $i in range($aFunc(5)): $i#slurp\n"*2,
+ "01234"*2)
+
+ def test16(self):
+ """false single line #for """
+ self.verify("#for $i in range(5): \n$i\n#end for",
+ "0\n1\n2\n3\n4\n")
+
+if versionTuple < (2,3):
+ del ForDirective.test12
+
+class RepeatDirective(OutputTest):
+
+ def test1(self):
+ """basic #repeat"""
+ self.verify("#repeat 3\n1\n#end repeat",
+ "1\n1\n1\n")
+ self.verify("#repeat 3: \n1\n#end repeat",
+ "1\n1\n1\n")
+
+ self.verify("#repeat 3 ##comment\n1\n#end repeat",
+ "1\n1\n1\n")
+
+ self.verify("#repeat 3: ##comment\n1\n#end repeat",
+ "1\n1\n1\n")
+
+ def test2(self):
+ """#repeat with numeric expression"""
+ self.verify("#repeat 3*3/3\n1\n#end repeat",
+ "1\n1\n1\n")
+
+ def test3(self):
+ """#repeat with placeholder"""
+ self.verify("#repeat $numTwo\n1\n#end repeat",
+ "1\n1\n")
+
+ def test4(self):
+ """#repeat with placeholder * num"""
+ self.verify("#repeat $numTwo*1\n1\n#end repeat",
+ "1\n1\n")
+
+ def test5(self):
+ """#repeat with placeholder and WS"""
+ self.verify(" #repeat $numTwo \n1\n #end repeat ",
+ "1\n1\n")
+
+ def test6(self):
+ """single-line #repeat"""
+ self.verify("#repeat $numTwo: 1",
+ "11")
+ self.verify("#repeat $numTwo: 1\n"*2,
+ "1\n1\n"*2)
+
+ #false single-line
+ self.verify("#repeat 3: \n1\n#end repeat",
+ "1\n1\n1\n")
+
+
+class AttrDirective(OutputTest):
+
+ def test1(self):
+ """#attr with int"""
+ self.verify("#attr $test = 1234\n$test",
+ "1234")
+
+ def test2(self):
+ """#attr with string"""
+ self.verify("#attr $test = 'blarg'\n$test",
+ "blarg")
+
+ def test3(self):
+ """#attr with expression"""
+ self.verify("#attr $test = 'blarg'.upper()*2\n$test",
+ "BLARGBLARG")
+
+ def test4(self):
+ """#attr with string + WS
+ Should gobble"""
+ self.verify(" #attr $test = 'blarg' \n$test",
+ "blarg")
+
+ def test5(self):
+ """#attr with string + WS + leading text
+ Shouldn't gobble"""
+ self.verify(" -- #attr $test = 'blarg' \n$test",
+ " -- \nblarg")
+
+
+class DefDirective(OutputTest):
+
+ def test1(self):
+ """#def without argstring"""
+ self.verify("#def testMeth\n1234\n#end def\n$testMeth",
+ "1234\n")
+
+ self.verify("#def testMeth ## comment\n1234\n#end def\n$testMeth",
+ "1234\n")
+
+ self.verify("#def testMeth: ## comment\n1234\n#end def\n$testMeth",
+ "1234\n")
+
+ def test2(self):
+ """#def without argstring, gobble WS"""
+ self.verify(" #def testMeth \n1234\n #end def \n$testMeth",
+ "1234\n")
+
+ def test3(self):
+ """#def with argstring, gobble WS"""
+ self.verify(" #def testMeth($a=999) \n1234-$a\n #end def\n$testMeth",
+ "1234-999\n")
+
+ def test4(self):
+ """#def with argstring, gobble WS, string used in call"""
+ self.verify(" #def testMeth($a=999) \n1234-$a\n #end def\n$testMeth('ABC')",
+ "1234-ABC\n")
+
+ def test5(self):
+ """#def with argstring, gobble WS, list used in call"""
+ self.verify(" #def testMeth($a=999) \n1234-$a\n #end def\n$testMeth([1,2,3])",
+ "1234-[1, 2, 3]\n")
+
+ def test6(self):
+ """#def with 2 args, gobble WS, list used in call"""
+ self.verify(" #def testMeth($a, $b='default') \n1234-$a$b\n #end def\n$testMeth([1,2,3])",
+ "1234-[1, 2, 3]default\n")
+
+ def test7(self):
+ """#def with *args, gobble WS"""
+ self.verify(" #def testMeth($*args) \n1234-$args\n #end def\n$testMeth",
+ "1234-()\n")
+
+ def test8(self):
+ """#def with **KWs, gobble WS"""
+ self.verify(" #def testMeth($**KWs) \n1234-$KWs\n #end def\n$testMeth",
+ "1234-{}\n")
+
+ def test9(self):
+ """#def with *args + **KWs, gobble WS"""
+ self.verify(" #def testMeth($*args, $**KWs) \n1234-$args-$KWs\n #end def\n$testMeth",
+ "1234-()-{}\n")
+
+ def test10(self):
+ """#def with *args + **KWs, gobble WS"""
+ self.verify(
+ " #def testMeth($*args, $**KWs) \n1234-$args-$KWs.a\n #end def\n$testMeth(1,2, a=1)",
+ "1234-(1, 2)-1\n")
+
+
+ def test11(self):
+ """single line #def with extra WS"""
+ self.verify(
+ "#def testMeth: aoeuaoeu\n- $testMeth -",
+ "- aoeuaoeu -")
+
+ def test12(self):
+ """single line #def with extra WS and nested $placeholders"""
+ self.verify(
+ "#def testMeth: $anInt $aFunc(1234)\n- $testMeth -",
+ "- 1 1234 -")
+
+ def test13(self):
+ """single line #def escaped $placeholders"""
+ self.verify(
+ "#def testMeth: \$aFunc(\$anInt)\n- $testMeth -",
+ "- $aFunc($anInt) -")
+
+ def test14(self):
+ """single line #def 1 escaped $placeholders"""
+ self.verify(
+ "#def testMeth: \$aFunc($anInt)\n- $testMeth -",
+ "- $aFunc(1) -")
+
+ def test15(self):
+ """single line #def 1 escaped $placeholders + more WS"""
+ self.verify(
+ "#def testMeth : \$aFunc($anInt)\n- $testMeth -",
+ "- $aFunc(1) -")
+
+ def test16(self):
+ """multiline #def with $ on methodName"""
+ self.verify("#def $testMeth\n1234\n#end def\n$testMeth",
+ "1234\n")
+
+ def test17(self):
+ """single line #def with $ on methodName"""
+ self.verify("#def $testMeth:1234\n$testMeth",
+ "1234")
+
+ def test18(self):
+ """single line #def with an argument"""
+ self.verify("#def $testMeth($arg=1234):$arg\n$testMeth",
+ "1234")
+
+
+class DecoratorDirective(OutputTest):
+ def test1(self):
+ """single line #def with decorator"""
+ self.verify("#from Cheetah.Tests.SyntaxAndOutput import testdecorator\n"
+ +"#@testdecorator"
+ +"\n#def $testMeth():1234\n$testMeth",
+
+ "1234")
+
+ self.verify("#from Cheetah.Tests.SyntaxAndOutput import testdecorator\n"
+ +"#@testdecorator"
+ +"\n#block $testMeth():1234",
+
+ "1234")
+
+ try:
+ self.verify(
+ "#from Cheetah.Tests.SyntaxAndOutput import testdecorator\n"
+ +"#@testdecorator\n sdf"
+ +"\n#def $testMeth():1234\n$testMeth",
+
+ "1234")
+ except ParseError:
+ pass
+ else:
+ self.fail('should raise a ParseError')
+
+if versionTuple < (2,4):
+ del DecoratorDirective
+
+class BlockDirective(OutputTest):
+
+ def test1(self):
+ """#block without argstring"""
+ self.verify("#block testBlock\n1234\n#end block",
+ "1234\n")
+
+ self.verify("#block testBlock ##comment\n1234\n#end block",
+ "1234\n")
+
+ def test2(self):
+ """#block without argstring, gobble WS"""
+ self.verify(" #block testBlock \n1234\n #end block ",
+ "1234\n")
+
+ def test3(self):
+ """#block with argstring, gobble WS
+
+ Because blocks can be reused in multiple parts of the template arguments
+ (!!with defaults!!) can be given."""
+
+ self.verify(" #block testBlock($a=999) \n1234-$a\n #end block ",
+ "1234-999\n")
+
+ def test4(self):
+ """#block with 2 args, gobble WS"""
+ self.verify(" #block testBlock($a=999, $b=444) \n1234-$a$b\n #end block ",
+ "1234-999444\n")
+
+
+ def test5(self):
+ """#block with 2 nested blocks
+
+ Blocks can be nested to any depth and the name of the block is optional
+ for the #end block part: #end block OR #end block [name] """
+
+ self.verify("""#block testBlock
+this is a test block
+#block outerNest
+outer
+#block innerNest
+inner
+#end block innerNest
+#end block outerNest
+---
+#end block testBlock
+""",
+ "this is a test block\nouter\ninner\n---\n")
+
+
+ def test6(self):
+ """single line #block """
+ self.verify(
+ "#block testMeth: This is my block",
+ "This is my block")
+
+ def test7(self):
+ """single line #block with WS"""
+ self.verify(
+ "#block testMeth: This is my block",
+ "This is my block")
+
+ def test8(self):
+ """single line #block 1 escaped $placeholders"""
+ self.verify(
+ "#block testMeth: \$aFunc($anInt)",
+ "$aFunc(1)")
+
+ def test9(self):
+ """single line #block 1 escaped $placeholders + WS"""
+ self.verify(
+ "#block testMeth: \$aFunc( $anInt )",
+ "$aFunc( 1 )")
+
+ def test10(self):
+ """single line #block 1 escaped $placeholders + more WS"""
+ self.verify(
+ "#block testMeth : \$aFunc( $anInt )",
+ "$aFunc( 1 )")
+
+ def test11(self):
+ """multiline #block $ on argstring"""
+ self.verify("#block $testBlock\n1234\n#end block",
+ "1234\n")
+
+ def test12(self):
+ """single line #block with $ on methodName """
+ self.verify(
+ "#block $testMeth: This is my block",
+ "This is my block")
+
+ def test13(self):
+ """single line #block with an arg """
+ self.verify(
+ "#block $testMeth($arg='This is my block'): $arg",
+ "This is my block")
+
+ def test14(self):
+ """single line #block with None for content"""
+ self.verify(
+ """#block $testMeth: $None\ntest $testMeth-""",
+ "test -")
+
+ def test15(self):
+ """single line #block with nothing for content"""
+ self.verify(
+ """#block $testMeth: \nfoo\n#end block\ntest $testMeth-""",
+ "foo\ntest foo\n-")
+
+class IncludeDirective(OutputTest):
+
+ def setUp(self):
+ fp = open('parseTest.txt','w')
+ fp.write("$numOne $numTwo")
+ fp.flush()
+ fp.close
+
+ def tearDown(self):
+ if os.path.exists('parseTest.txt'):
+ os.remove('parseTest.txt')
+
+ def test1(self):
+ """#include raw of source $emptyString"""
+ self.verify("#include raw source=$emptyString",
+ "")
+
+ def test2(self):
+ """#include raw of source $blockToBeParsed"""
+ self.verify("#include raw source=$blockToBeParsed",
+ "$numOne $numTwo")
+
+ def test3(self):
+ """#include raw of 'parseTest.txt'"""
+ self.verify("#include raw 'parseTest.txt'",
+ "$numOne $numTwo")
+
+ def test4(self):
+ """#include raw of $includeFileName"""
+ self.verify("#include raw $includeFileName",
+ "$numOne $numTwo")
+
+ def test5(self):
+ """#include raw of $includeFileName, with WS"""
+ self.verify(" #include raw $includeFileName ",
+ "$numOne $numTwo")
+
+ def test6(self):
+ """#include raw of source= , with WS"""
+ self.verify(" #include raw source='This is my $Source '*2 ",
+ "This is my $Source This is my $Source ")
+
+ def test7(self):
+ """#include of $blockToBeParsed"""
+ self.verify("#include source=$blockToBeParsed",
+ "1 2")
+
+ def test8(self):
+ """#include of $blockToBeParsed, with WS"""
+ self.verify(" #include source=$blockToBeParsed ",
+ "1 2")
+
+ def test9(self):
+ """#include of 'parseTest.txt', with WS"""
+ self.verify(" #include source=$blockToBeParsed ",
+ "1 2")
+
+ def test10(self):
+ """#include of "parseTest.txt", with WS"""
+ self.verify(" #include source=$blockToBeParsed ",
+ "1 2")
+
+ def test11(self):
+ """#include of 'parseTest.txt', with WS and surrounding text"""
+ self.verify("aoeu\n #include source=$blockToBeParsed \naoeu",
+ "aoeu\n1 2aoeu")
+
+ def test12(self):
+ """#include of 'parseTest.txt', with WS and explicit closure"""
+ self.verify(" #include source=$blockToBeParsed# ",
+ " 1 2 ")
+
+
+class SilentDirective(OutputTest):
+
+ def test1(self):
+ """simple #silent"""
+ self.verify("#silent $aFunc",
+ "")
+
+ def test2(self):
+ """simple #silent"""
+ self.verify("#silent $anObj.callIt\n$anObj.callArg",
+ "1234")
+
+ self.verify("#silent $anObj.callIt ##comment\n$anObj.callArg",
+ "1234")
+
+ def test3(self):
+ """simple #silent"""
+ self.verify("#silent $anObj.callIt(99)\n$anObj.callArg",
+ "99")
+
+class SetDirective(OutputTest):
+
+ def test1(self):
+ """simple #set"""
+ self.verify("#set $testVar = 'blarg'\n$testVar",
+ "blarg")
+ self.verify("#set testVar = 'blarg'\n$testVar",
+ "blarg")
+
+
+ self.verify("#set testVar = 'blarg'##comment\n$testVar",
+ "blarg")
+
+ def test2(self):
+ """simple #set with no WS between operands"""
+ self.verify("#set $testVar='blarg'",
+ "")
+ def test3(self):
+ """#set + use of var"""
+ self.verify("#set $testVar = 'blarg'\n$testVar",
+ "blarg")
+
+ def test4(self):
+ """#set + use in an #include"""
+ self.verify("#set global $aSetVar = 1234\n#include source=$includeBlock2",
+ "1 2 1234")
+
+ def test5(self):
+ """#set with a dictionary"""
+ self.verify( """#set $testDict = {'one':'one1','two':'two2','three':'three3'}
+$testDict.one
+$testDict.two""",
+ "one1\ntwo2")
+
+ def test6(self):
+ """#set with string, then used in #if block"""
+
+ self.verify("""#set $test='a string'\n#if $test#blarg#end if""",
+ "blarg")
+
+ def test7(self):
+ """simple #set, gobble WS"""
+ self.verify(" #set $testVar = 'blarg' ",
+ "")
+
+ def test8(self):
+ """simple #set, don't gobble WS"""
+ self.verify(" #set $testVar = 'blarg'#---",
+ " ---")
+
+ def test9(self):
+ """simple #set with a list"""
+ self.verify(" #set $testVar = [1, 2, 3] \n$testVar",
+ "[1, 2, 3]")
+
+ def test10(self):
+ """simple #set global with a list"""
+ self.verify(" #set global $testVar = [1, 2, 3] \n$testVar",
+ "[1, 2, 3]")
+
+ def test11(self):
+ """simple #set global with a list and *cache
+
+ Caching only works with global #set vars. Local vars are not accesible
+ to the cache namespace.
+ """
+
+ self.verify(" #set global $testVar = [1, 2, 3] \n$*testVar",
+ "[1, 2, 3]")
+
+ def test12(self):
+ """simple #set global with a list and *<int>*cache"""
+ self.verify(" #set global $testVar = [1, 2, 3] \n$*5*testVar",
+ "[1, 2, 3]")
+
+ def test13(self):
+ """simple #set with a list and *<float>*cache"""
+ self.verify(" #set global $testVar = [1, 2, 3] \n$*.5*testVar",
+ "[1, 2, 3]")
+
+ def test14(self):
+ """simple #set without NameMapper on"""
+ self.verify("""#compiler useNameMapper = 0\n#set $testVar = 1 \n$testVar""",
+ "1")
+
+ def test15(self):
+ """simple #set without $"""
+ self.verify("""#set testVar = 1 \n$testVar""",
+ "1")
+
+ def test16(self):
+ """simple #set global without $"""
+ self.verify("""#set global testVar = 1 \n$testVar""",
+ "1")
+
+ def test17(self):
+ """simple #set module without $"""
+ self.verify("""#set module __foo__ = 'bar'\n$__foo__""",
+ "bar")
+
+ def test18(self):
+ """#set with i,j=list style assignment"""
+ self.verify("""#set i,j = [1,2]\n$i$j""",
+ "12")
+ self.verify("""#set $i,$j = [1,2]\n$i$j""",
+ "12")
+
+ def test19(self):
+ """#set with (i,j)=list style assignment"""
+ self.verify("""#set (i,j) = [1,2]\n$i$j""",
+ "12")
+ self.verify("""#set ($i,$j) = [1,2]\n$i$j""",
+ "12")
+
+ def test20(self):
+ """#set with i, (j,k)=list style assignment"""
+ self.verify("""#set i, (j,k) = [1,(2,3)]\n$i$j$k""",
+ "123")
+ self.verify("""#set $i, ($j,$k) = [1,(2,3)]\n$i$j$k""",
+ "123")
+
+
+class IfDirective(OutputTest):
+
+ def test1(self):
+ """simple #if block"""
+ self.verify("#if 1\n$aStr\n#end if\n",
+ "blarg\n")
+
+ self.verify("#if 1:\n$aStr\n#end if\n",
+ "blarg\n")
+
+ self.verify("#if 1: \n$aStr\n#end if\n",
+ "blarg\n")
+
+ self.verify("#if 1: ##comment \n$aStr\n#end if\n",
+ "blarg\n")
+
+ self.verify("#if 1 ##comment \n$aStr\n#end if\n",
+ "blarg\n")
+
+ self.verify("#if 1##for i in range(10)#$i#end for##end if",
+ '0123456789')
+
+ self.verify("#if 1: #for i in range(10)#$i#end for",
+ '0123456789')
+
+ self.verify("#if 1: #for i in range(10):$i",
+ '0123456789')
+
+ def test2(self):
+ """simple #if block, with WS"""
+ self.verify(" #if 1\n$aStr\n #end if \n",
+ "blarg\n")
+ def test3(self):
+ """simple #if block, with WS and explicit closures"""
+ self.verify(" #if 1#\n$aStr\n #end if #--\n",
+ " \nblarg\n --\n")
+
+ def test4(self):
+ """#if block using $numOne"""
+ self.verify("#if $numOne\n$aStr\n#end if\n",
+ "blarg\n")
+
+ def test5(self):
+ """#if block using $zero"""
+ self.verify("#if $zero\n$aStr\n#end if\n",
+ "")
+ def test6(self):
+ """#if block using $emptyString"""
+ self.verify("#if $emptyString\n$aStr\n#end if\n",
+ "")
+ def test7(self):
+ """#if ... #else ... block using a $emptyString"""
+ self.verify("#if $emptyString\n$anInt\n#else\n$anInt - $anInt\n#end if",
+ "1 - 1\n")
+
+ def test8(self):
+ """#if ... #elif ... #else ... block using a $emptyString"""
+ self.verify("#if $emptyString\n$c\n#elif $numOne\n$numOne\n#else\n$c - $c\n#end if",
+ "1\n")
+
+ def test9(self):
+ """#if 'not' test, with #slurp"""
+ self.verify("#if not $emptyString\n$aStr#slurp\n#end if\n",
+ "blarg")
+
+ def test10(self):
+ """#if block using $*emptyString
+
+ This should barf
+ """
+ try:
+ self.verify("#if $*emptyString\n$aStr\n#end if\n",
+ "")
+ except ParseError:
+ pass
+ else:
+ self.fail('This should barf')
+
+ def test11(self):
+ """#if block using invalid top-level $(placeholder) syntax - should barf"""
+
+ for badSyntax in ("#if $*5*emptyString\n$aStr\n#end if\n",
+ "#if ${emptyString}\n$aStr\n#end if\n",
+ "#if $(emptyString)\n$aStr\n#end if\n",
+ "#if $[emptyString]\n$aStr\n#end if\n",
+ "#if $!emptyString\n$aStr\n#end if\n",
+ ):
+ try:
+ self.verify(badSyntax, "")
+ except ParseError:
+ pass
+ else:
+ self.fail('This should barf')
+
+ def test12(self):
+ """#if ... #else if ... #else ... block using a $emptyString
+ Same as test 8 but using else if instead of elif"""
+ self.verify("#if $emptyString\n$c\n#else if $numOne\n$numOne\n#else\n$c - $c\n#end if",
+ "1\n")
+
+
+ def test13(self):
+ """#if# ... #else # ... block using a $emptyString with """
+ self.verify("#if $emptyString# $anInt#else#$anInt - $anInt#end if",
+ "1 - 1")
+
+ def test14(self):
+ """single-line #if: simple"""
+ self.verify("#if $emptyString then 'true' else 'false'",
+ "false")
+
+ def test15(self):
+ """single-line #if: more complex"""
+ self.verify("#if $anInt then 'true' else 'false'",
+ "true")
+
+ def test16(self):
+ """single-line #if: with the words 'else' and 'then' in the output """
+ self.verify("#if ($anInt and not $emptyString==''' else ''') then $str('then') else 'else'",
+ "then")
+
+ def test17(self):
+ """single-line #if: """
+ self.verify("#if 1: foo\n#if 0: bar\n#if 1: foo",
+ "foo\nfoo")
+
+
+ self.verify("#if 1: foo\n#if 0: bar\n#if 1: foo",
+ "foo\nfoo")
+
+ def test18(self):
+ """single-line #if: \n#else: """
+ self.verify("#if 1: foo\n#elif 0: bar",
+ "foo\n")
+
+ self.verify("#if 1: foo\n#elif 0: bar\n#else: blarg\n",
+ "foo\n")
+
+ self.verify("#if 0: foo\n#elif 0: bar\n#else: blarg\n",
+ "blarg\n")
+
+class UnlessDirective(OutputTest):
+
+ def test1(self):
+ """#unless 1"""
+ self.verify("#unless 1\n 1234 \n#end unless",
+ "")
+
+ self.verify("#unless 1:\n 1234 \n#end unless",
+ "")
+
+ self.verify("#unless 1: ##comment\n 1234 \n#end unless",
+ "")
+
+ self.verify("#unless 1 ##comment\n 1234 \n#end unless",
+ "")
+
+
+ def test2(self):
+ """#unless 0"""
+ self.verify("#unless 0\n 1234 \n#end unless",
+ " 1234 \n")
+
+ def test3(self):
+ """#unless $none"""
+ self.verify("#unless $none\n 1234 \n#end unless",
+ " 1234 \n")
+
+ def test4(self):
+ """#unless $numTwo"""
+ self.verify("#unless $numTwo\n 1234 \n#end unless",
+ "")
+
+ def test5(self):
+ """#unless $numTwo with WS"""
+ self.verify(" #unless $numTwo \n 1234 \n #end unless ",
+ "")
+
+ def test6(self):
+ """single-line #unless"""
+ self.verify("#unless 1: 1234", "")
+ self.verify("#unless 0: 1234", "1234")
+ self.verify("#unless 0: 1234\n"*2, "1234\n"*2)
+
+class PSP(OutputTest):
+
+ def test1(self):
+ """simple <%= [int] %>"""
+ self.verify("<%= 1234 %>", "1234")
+
+ def test2(self):
+ """simple <%= [string] %>"""
+ self.verify("<%= 'blarg' %>", "blarg")
+
+ def test3(self):
+ """simple <%= None %>"""
+ self.verify("<%= None %>", "")
+ def test4(self):
+ """simple <%= [string] %> + $anInt"""
+ self.verify("<%= 'blarg' %>$anInt", "blarg1")
+
+ def test5(self):
+ """simple <%= [EXPR] %> + $anInt"""
+ self.verify("<%= ('blarg'*2).upper() %>$anInt", "BLARGBLARG1")
+
+ def test6(self):
+ """for loop in <%%>"""
+ self.verify("<% for i in range(5):%>1<%end%>", "11111")
+
+ def test7(self):
+ """for loop in <%%> and using <%=i%>"""
+ self.verify("<% for i in range(5):%><%=i%><%end%>", "01234")
+
+ def test8(self):
+ """for loop in <% $%> and using <%=i%>"""
+ self.verify("""<% for i in range(5):
+ i=i*2$%><%=i%><%end%>""", "02468")
+
+ def test9(self):
+ """for loop in <% $%> and using <%=i%> plus extra text"""
+ self.verify("""<% for i in range(5):
+ i=i*2$%><%=i%>-<%end%>""", "0-2-4-6-8-")
+
+
+class WhileDirective(OutputTest):
+ def test1(self):
+ """simple #while with a counter"""
+ self.verify("#set $i = 0\n#while $i < 5\n$i#slurp\n#set $i += 1\n#end while",
+ "01234")
+
+class ContinueDirective(OutputTest):
+ def test1(self):
+ """#continue with a #while"""
+ self.verify("""#set $i = 0
+#while $i < 5
+#if $i == 3
+ #set $i += 1
+ #continue
+#end if
+$i#slurp
+#set $i += 1
+#end while""",
+ "0124")
+
+ def test2(self):
+ """#continue with a #for"""
+ self.verify("""#for $i in range(5)
+#if $i == 3
+ #continue
+#end if
+$i#slurp
+#end for""",
+ "0124")
+
+class BreakDirective(OutputTest):
+ def test1(self):
+ """#break with a #while"""
+ self.verify("""#set $i = 0
+#while $i < 5
+#if $i == 3
+ #break
+#end if
+$i#slurp
+#set $i += 1
+#end while""",
+ "012")
+
+ def test2(self):
+ """#break with a #for"""
+ self.verify("""#for $i in range(5)
+#if $i == 3
+ #break
+#end if
+$i#slurp
+#end for""",
+ "012")
+
+
+class TryDirective(OutputTest):
+
+ def test1(self):
+ """simple #try
+ """
+ self.verify("#try\n1234\n#except\nblarg\n#end try",
+ "1234\n")
+
+ def test2(self):
+ """#try / #except with #raise
+ """
+ self.verify("#try\n#raise ValueError\n#except\nblarg\n#end try",
+ "blarg\n")
+
+ def test3(self):
+ """#try / #except with #raise + WS
+
+ Should gobble
+ """
+ self.verify(" #try \n #raise ValueError \n #except \nblarg\n #end try",
+ "blarg\n")
+
+
+ def test4(self):
+ """#try / #except with #raise + WS and leading text
+
+ Shouldn't gobble
+ """
+ self.verify("--#try \n #raise ValueError \n #except \nblarg\n #end try#--",
+ "--\nblarg\n --")
+
+ def test5(self):
+ """nested #try / #except with #raise
+ """
+ self.verify(
+"""#try
+ #raise ValueError
+#except
+ #try
+ #raise ValueError
+ #except
+blarg
+ #end try
+#end try""",
+ "blarg\n")
+
+class PassDirective(OutputTest):
+ def test1(self):
+ """#pass in a #try / #except block
+ """
+ self.verify("#try\n#raise ValueError\n#except\n#pass\n#end try",
+ "")
+
+ def test2(self):
+ """#pass in a #try / #except block + WS
+ """
+ self.verify(" #try \n #raise ValueError \n #except \n #pass \n #end try",
+ "")
+
+
+class AssertDirective(OutputTest):
+ def test1(self):
+ """simple #assert
+ """
+ self.verify("#set $x = 1234\n#assert $x == 1234",
+ "")
+
+ def test2(self):
+ """simple #assert that fails
+ """
+ def test(self=self):
+ self.verify("#set $x = 1234\n#assert $x == 999",
+ ""),
+ self.failUnlessRaises(AssertionError, test)
+
+ def test3(self):
+ """simple #assert with WS
+ """
+ self.verify("#set $x = 1234\n #assert $x == 1234 ",
+ "")
+
+
+class RaiseDirective(OutputTest):
+ def test1(self):
+ """simple #raise ValueError
+
+ Should raise ValueError
+ """
+ def test(self=self):
+ self.verify("#raise ValueError",
+ ""),
+ self.failUnlessRaises(ValueError, test)
+
+ def test2(self):
+ """#raise ValueError in #if block
+
+ Should raise ValueError
+ """
+ def test(self=self):
+ self.verify("#if 1\n#raise ValueError\n#end if\n",
+ "")
+ self.failUnlessRaises(ValueError, test)
+
+
+ def test3(self):
+ """#raise ValueError in #if block
+
+ Shouldn't raise ValueError
+ """
+ self.verify("#if 0\n#raise ValueError\n#else\nblarg#end if\n",
+ "blarg\n")
+
+
+
+class ImportDirective(OutputTest):
+ def test1(self):
+ """#import math
+ """
+ self.verify("#import math",
+ "")
+
+ def test2(self):
+ """#import math + WS
+
+ Should gobble
+ """
+ self.verify(" #import math ",
+ "")
+
+ def test3(self):
+ """#import math + WS + leading text
+
+ Shouldn't gobble
+ """
+ self.verify(" -- #import math ",
+ " -- ")
+
+ def test4(self):
+ """#from math import syn
+ """
+ self.verify("#from math import cos",
+ "")
+
+ def test5(self):
+ """#from math import cos + WS
+ Should gobble
+ """
+ self.verify(" #from math import cos ",
+ "")
+
+ def test6(self):
+ """#from math import cos + WS + leading text
+ Shouldn't gobble
+ """
+ self.verify(" -- #from math import cos ",
+ " -- ")
+
+ def test7(self):
+ """#from math import cos -- use it
+ """
+ self.verify("#from math import cos\n$cos(0)",
+ "1.0")
+
+ def test8(self):
+ """#from math import cos,tan,sin -- and use them
+ """
+ self.verify("#from math import cos, tan, sin\n$cos(0)-$tan(0)-$sin(0)",
+ "1.0-0.0-0.0")
+
+ def test9(self):
+ """#import os.path -- use it
+ """
+
+ self.verify("#import os.path\n$os.path.exists('.')",
+ repr(True))
+
+ def test10(self):
+ """#import os.path -- use it with NameMapper turned off
+ """
+ self.verify("""##
+#compiler-settings
+useNameMapper=False
+#end compiler-settings
+#import os.path
+$os.path.exists('.')""",
+ repr(True))
+
+ def test11(self):
+ """#from math import *
+ """
+
+ self.verify("#from math import *\n$pow(1,2) $log10(10)",
+ "1.0 1.0")
+
+class CompilerDirective(OutputTest):
+ def test1(self):
+ """overriding the commentStartToken
+ """
+ self.verify("""$anInt##comment
+#compiler commentStartToken = '//'
+$anInt//comment
+""",
+ "1\n1\n")
+
+ def test2(self):
+ """overriding and resetting the commentStartToken
+ """
+ self.verify("""$anInt##comment
+#compiler commentStartToken = '//'
+$anInt//comment
+#compiler reset
+$anInt//comment
+""",
+ "1\n1\n1//comment\n")
+
+
+class CompilerSettingsDirective(OutputTest):
+
+ def test1(self):
+ """overriding the cheetahVarStartToken
+ """
+ self.verify("""$anInt
+#compiler-settings
+cheetahVarStartToken = @
+#end compiler-settings
+@anInt
+#compiler-settings reset
+$anInt
+""",
+ "1\n1\n1\n")
+
+ def test2(self):
+ """overriding the directiveStartToken
+ """
+ self.verify("""#set $x = 1234
+$x
+#compiler-settings
+directiveStartToken = @
+#end compiler-settings
+@set $x = 1234
+$x
+""",
+ "1234\n1234\n")
+
+ def test3(self):
+ """overriding the commentStartToken
+ """
+ self.verify("""$anInt##comment
+#compiler-settings
+commentStartToken = //
+#end compiler-settings
+$anInt//comment
+""",
+ "1\n1\n")
+
+if sys.platform.startswith('java'):
+ del CompilerDirective
+ del CompilerSettingsDirective
+
+class ExtendsDirective(OutputTest):
+
+ def test1(self):
+ """#extends Cheetah.Templates._SkeletonPage"""
+ self.verify("""#from Cheetah.Templates._SkeletonPage import _SkeletonPage
+#extends _SkeletonPage
+#implements respond
+$spacer()
+""",
+ '<img src="spacer.gif" width="1" height="1" alt="" />\n')
+
+
+ self.verify("""#from Cheetah.Templates._SkeletonPage import _SkeletonPage
+#extends _SkeletonPage
+#implements respond(foo=1234)
+$spacer()$foo
+""",
+ '<img src="spacer.gif" width="1" height="1" alt="" />1234\n')
+
+ def test2(self):
+ """#extends Cheetah.Templates.SkeletonPage without #import"""
+ self.verify("""#extends Cheetah.Templates.SkeletonPage
+#implements respond
+$spacer()
+""",
+ '<img src="spacer.gif" width="1" height="1" alt="" />\n')
+
+ def test3(self):
+ """#extends Cheetah.Templates.SkeletonPage.SkeletonPage without #import"""
+ self.verify("""#extends Cheetah.Templates.SkeletonPage.SkeletonPage
+#implements respond
+$spacer()
+""",
+ '<img src="spacer.gif" width="1" height="1" alt="" />\n')
+
+ def test4(self):
+ """#extends with globals and searchList test"""
+ self.verify("""#extends Cheetah.Templates.SkeletonPage
+#set global g="Hello"
+#implements respond
+$g $numOne
+""",
+ 'Hello 1\n')
+
+class ImportantExampleCases(OutputTest):
+ def test1(self):
+ """how to make a comma-delimited list"""
+ self.verify("""#set $sep = ''
+#for $letter in $letterList
+$sep$letter#slurp
+#set $sep = ', '
+#end for
+""",
+ "a, b, c")
+
+class FilterDirective(OutputTest):
+ convertEOLs=False
+
+ def _getCompilerSettings(self):
+ return {'useFilterArgsInPlaceholders':True}
+
+ def test1(self):
+ """#filter ReplaceNone
+ """
+ self.verify("#filter ReplaceNone\n$none#end filter",
+ "")
+
+ self.verify("#filter ReplaceNone: $none",
+ "")
+
+ def test2(self):
+ """#filter ReplaceNone with WS
+ """
+ self.verify("#filter ReplaceNone \n$none#end filter",
+ "")
+
+ def test3(self):
+ """#filter MaxLen -- maxlen of 5"""
+
+ self.verify("#filter MaxLen \n${tenDigits, $maxlen=5}#end filter",
+ "12345")
+
+ def test4(self):
+ """#filter MaxLen -- no maxlen
+ """
+ self.verify("#filter MaxLen \n${tenDigits}#end filter",
+ "1234567890")
+
+ def test5(self):
+ """#filter WebSafe -- basic usage
+ """
+ self.verify("#filter WebSafe \n$webSafeTest#end filter",
+ "abc &lt;=&gt; &amp;")
+
+ def test6(self):
+ """#filter WebSafe -- also space
+ """
+ self.verify("#filter WebSafe \n${webSafeTest, $also=' '}#end filter",
+ "abc&nbsp;&lt;=&gt;&nbsp;&amp;")
+
+ def test7(self):
+ """#filter WebSafe -- also space, without $ on the args
+ """
+ self.verify("#filter WebSafe \n${webSafeTest, also=' '}#end filter",
+ "abc&nbsp;&lt;=&gt;&nbsp;&amp;")
+
+ def test8(self):
+ """#filter Strip -- trailing newline
+ """
+ self.verify("#filter Strip\n$strip1#end filter",
+ "strippable whitespace\n")
+
+ def test9(self):
+ """#filter Strip -- no trailing newine
+ """
+ self.verify("#filter Strip\n$strip2#end filter",
+ "strippable whitespace")
+
+ def test10(self):
+ """#filter Strip -- multi-line
+ """
+ self.verify("#filter Strip\n$strip3#end filter",
+ "strippable whitespace\n1 2 3\n")
+
+ def test11(self):
+ """#filter StripSqueeze -- canonicalize all whitespace to ' '
+ """
+ self.verify("#filter StripSqueeze\n$strip3#end filter",
+ "strippable whitespace 1 2 3")
+
+
+class EchoDirective(OutputTest):
+ def test1(self):
+ """#echo 1234
+ """
+ self.verify("#echo 1234",
+ "1234")
+
+class SilentDirective(OutputTest):
+ def test1(self):
+ """#silent 1234
+ """
+ self.verify("#silent 1234",
+ "")
+
+class ErrorCatcherDirective(OutputTest):
+ pass
+
+
+class VarExists(OutputTest): # Template.varExists()
+
+ def test1(self):
+ """$varExists('$anInt')
+ """
+ self.verify("$varExists('$anInt')",
+ repr(True))
+
+ def test2(self):
+ """$varExists('anInt')
+ """
+ self.verify("$varExists('anInt')",
+ repr(True))
+
+ def test3(self):
+ """$varExists('$anInt')
+ """
+ self.verify("$varExists('$bogus')",
+ repr(False))
+
+ def test4(self):
+ """$varExists('$anInt') combined with #if false
+ """
+ self.verify("#if $varExists('$bogus')\n1234\n#else\n999\n#end if",
+ "999\n")
+
+ def test5(self):
+ """$varExists('$anInt') combined with #if true
+ """
+ self.verify("#if $varExists('$anInt')\n1234\n#else\n999#end if",
+ "1234\n")
+
+class GetVar(OutputTest): # Template.getVar()
+ def test1(self):
+ """$getVar('$anInt')
+ """
+ self.verify("$getVar('$anInt')",
+ "1")
+
+ def test2(self):
+ """$getVar('anInt')
+ """
+ self.verify("$getVar('anInt')",
+ "1")
+
+ def test3(self):
+ """$self.getVar('anInt')
+ """
+ self.verify("$self.getVar('anInt')",
+ "1")
+
+ def test4(self):
+ """$getVar('bogus', 1234)
+ """
+ self.verify("$getVar('bogus', 1234)",
+ "1234")
+
+ def test5(self):
+ """$getVar('$bogus', 1234)
+ """
+ self.verify("$getVar('$bogus', 1234)",
+ "1234")
+
+
+class MiscComplexSyntax(OutputTest):
+ def test1(self):
+ """Complex use of {},[] and () in a #set expression
+ ----
+ #set $c = {'A':0}[{}.get('a', {'a' : 'A'}['a'])]
+ $c
+ """
+ self.verify("#set $c = {'A':0}[{}.get('a', {'a' : 'A'}['a'])]\n$c",
+ "0")
+
+
+class CGI(OutputTest):
+ """CGI scripts with(out) the CGI environment and with(out) GET variables.
+ """
+ convertEOLs=False
+
+ def _beginCGI(self):
+ os.environ['REQUEST_METHOD'] = "GET"
+ def _endCGI(self):
+ try:
+ del os.environ['REQUEST_METHOD']
+ except KeyError:
+ pass
+ _guaranteeNoCGI = _endCGI
+
+
+ def test1(self):
+ """A regular template."""
+ self._guaranteeNoCGI()
+ source = "#extends Cheetah.Tools.CGITemplate\n" + \
+ "#implements respond\n" + \
+ "$cgiHeaders#slurp\n" + \
+ "Hello, world!"
+ self.verify(source, "Hello, world!")
+
+
+ def test2(self):
+ """A CGI script."""
+ self._beginCGI()
+ source = "#extends Cheetah.Tools.CGITemplate\n" + \
+ "#implements respond\n" + \
+ "$cgiHeaders#slurp\n" + \
+ "Hello, world!"
+ self.verify(source, "Content-type: text/html\n\nHello, world!")
+ self._endCGI()
+
+
+ def test3(self):
+ """A (pseudo) Webware servlet.
+
+ This uses the Python syntax escape to set
+ self._CHEETAH__isControlledByWebKit.
+ We could instead do '#silent self._CHEETAH__isControlledByWebKit = True',
+ taking advantage of the fact that it will compile unchanged as long
+ as there's no '$' in the statement. (It won't compile with an '$'
+ because that would convert to a function call, and you can't assign
+ to a function call.) Because this isn't really being called from
+ Webware, we'd better not use any Webware services! Likewise, we'd
+ better not call $cgiImport() because it would be misled.
+ """
+ self._beginCGI()
+ source = "#extends Cheetah.Tools.CGITemplate\n" + \
+ "#implements respond\n" + \
+ "<% self._CHEETAH__isControlledByWebKit = True %>#slurp\n" + \
+ "$cgiHeaders#slurp\n" + \
+ "Hello, world!"
+ self.verify(source, "Hello, world!")
+ self._endCGI()
+
+
+ def test4(self):
+ """A CGI script with a GET variable."""
+ self._beginCGI()
+ os.environ['QUERY_STRING'] = "cgiWhat=world"
+ source = "#extends Cheetah.Tools.CGITemplate\n" + \
+ "#implements respond\n" + \
+ "$cgiHeaders#slurp\n" + \
+ "#silent $webInput(['cgiWhat'])##slurp\n" + \
+ "Hello, $cgiWhat!"
+ self.verify(source,
+ "Content-type: text/html\n\nHello, world!")
+ del os.environ['QUERY_STRING']
+ self._endCGI()
+
+
+
+class WhitespaceAfterDirectiveTokens(OutputTest):
+ def _getCompilerSettings(self):
+ return {'allowWhitespaceAfterDirectiveStartToken':True}
+
+ def test1(self):
+ self.verify("# for i in range(10): $i",
+ "0123456789")
+ self.verify("# for i in range(10)\n$i# end for",
+ "0123456789")
+ self.verify("# for i in range(10)#$i#end for",
+ "0123456789")
+
+
+
+class DefmacroDirective(OutputTest):
+ def _getCompilerSettings(self):
+ def aMacro(src):
+ return '$aStr'
+
+ return {'macroDirectives':{'aMacro':aMacro
+ }}
+
+ def test1(self):
+ self.verify("""\
+#defmacro inc: #set @src +=1
+#set i = 1
+#inc: $i
+$i""",
+ "2")
+
+
+
+ self.verify("""\
+#defmacro test
+#for i in range(10): @src
+#end defmacro
+#test: $i-foo#slurp
+#for i in range(3): $i""",
+ "0-foo1-foo2-foo3-foo4-foo5-foo6-foo7-foo8-foo9-foo012")
+
+ self.verify("""\
+#defmacro test
+#for i in range(10): @src
+#end defmacro
+#test: $i-foo
+#for i in range(3): $i""",
+ "0-foo\n1-foo\n2-foo\n3-foo\n4-foo\n5-foo\n6-foo\n7-foo\n8-foo\n9-foo\n012")
+
+
+ self.verify("""\
+#defmacro test: #for i in range(10): @src
+#test: $i-foo#slurp
+-#for i in range(3): $i""",
+ "0-foo1-foo2-foo3-foo4-foo5-foo6-foo7-foo8-foo9-foo-012")
+
+ self.verify("""\
+#defmacro test##for i in range(10): @src#end defmacro##slurp
+#test: $i-foo#slurp
+-#for i in range(3): $i""",
+ "0-foo1-foo2-foo3-foo4-foo5-foo6-foo7-foo8-foo9-foo-012")
+
+ self.verify("""\
+#defmacro testFoo: nothing
+#defmacro test(foo=1234): #for i in range(10): @src
+#test foo=234: $i-foo#slurp
+-#for i in range(3): $i""",
+ "0-foo1-foo2-foo3-foo4-foo5-foo6-foo7-foo8-foo9-foo-012")
+
+ self.verify("""\
+#defmacro testFoo: nothing
+#defmacro test(foo=1234): #for i in range(10): @src@foo
+#test foo='-foo'#$i#end test#-#for i in range(3): $i""",
+ "0-foo1-foo2-foo3-foo4-foo5-foo6-foo7-foo8-foo9-foo-012")
+
+ self.verify("""\
+#defmacro testFoo: nothing
+#defmacro test(foo=1234): #for i in range(10): @src.strip()@foo
+#test foo='-foo': $i
+-#for i in range(3): $i""",
+ "0-foo1-foo2-foo3-foo4-foo5-foo6-foo7-foo8-foo9-foo-012")
+
+ def test2(self):
+ self.verify("#aMacro: foo",
+ "blarg")
+ self.verify("#defmacro nested: @macros.aMacro(@src)\n#nested: foo",
+ "blarg")
+
+
+class Indenter(OutputTest):
+ convertEOLs=False
+
+ source = """
+public class X
+{
+ #for $method in $methods
+ $getMethod($method)
+
+ #end for
+}
+//end of class
+
+#def getMethod($method)
+ #indent ++
+ public $getType($method) ${method.Name}($getParams($method.Params));
+ #indent --
+#end def
+
+#def getParams($params)
+ #indent off
+
+ #for $counter in $range($len($params))
+ #if $counter == len($params) - 1
+ $params[$counter]#slurp
+ #else:
+ $params[$counter],
+ #end if
+ #end for
+ #indent on
+#end def
+
+#def getType($method)
+ #indent push
+ #indent=0
+ #if $method.Type == "VT_VOID"
+ void#slurp
+ #elif $method.Type == "VT_INT"
+ int#slurp
+ #elif $method.Type == "VT_VARIANT"
+ Object#slurp
+ #end if
+ #indent pop
+#end def
+"""
+
+ control = """
+public class X
+{
+ public void Foo(
+ _input,
+ _output);
+
+
+ public int Bar(
+ _str1,
+ str2,
+ _str3);
+
+
+ public Object Add(
+ value1,
+ value);
+
+
+}
+//end of class
+
+
+
+"""
+ def _getCompilerSettings(self):
+ return {'useFilterArgsInPlaceholders':True}
+
+ def searchList(self): # Inside Indenter class.
+ class Method:
+ def __init__(self, _name, _type, *_params):
+ self.Name = _name
+ self.Type = _type
+ self.Params = _params
+ methods = [Method("Foo", "VT_VOID", "_input", "_output"),
+ Method("Bar", "VT_INT", "_str1", "str2", "_str3"),
+ Method("Add", "VT_VARIANT", "value1", "value")]
+ return [{"methods": methods}]
+
+ def test1(self): # Inside Indenter class.
+ self.verify(self.source, self.control)
+
+
+##################################################
+## CREATE CONVERTED EOL VERSIONS OF THE TEST CASES
+
+if OutputTest._useNewStyleCompilation and versionTuple >= (2,3):
+ extraCompileKwArgsForDiffBaseclass = {'baseclass':dict}
+else:
+ extraCompileKwArgsForDiffBaseclass = {'baseclass':object}
+
+
+for klass in [var for var in globals().values()
+ if type(var) == types.ClassType and issubclass(var, unittest.TestCase)]:
+ name = klass.__name__
+ if hasattr(klass,'convertEOLs') and klass.convertEOLs:
+ win32Src = r"class %(name)s_Win32EOL(%(name)s): _EOLreplacement = '\r\n'"%locals()
+ macSrc = r"class %(name)s_MacEOL(%(name)s): _EOLreplacement = '\r'"%locals()
+ #print win32Src
+ #print macSrc
+ exec win32Src+'\n'
+ exec macSrc+'\n'
+
+ if versionTuple >= (2,3):
+ src = r"class %(name)s_DiffBaseClass(%(name)s): "%locals()
+ src += " _extraCompileKwArgs = extraCompileKwArgsForDiffBaseclass"
+ exec src+'\n'
+
+ del name
+ del klass
+
+##################################################
+## if run from the command line ##
+
+if __name__ == '__main__':
+ unittest.main()
+
+# vim: shiftwidth=4 tabstop=4 expandtab
diff --git a/cobbler/Cheetah/Tests/Template.py b/cobbler/Cheetah/Tests/Template.py
new file mode 100644
index 0000000..b97cf5d
--- /dev/null
+++ b/cobbler/Cheetah/Tests/Template.py
@@ -0,0 +1,312 @@
+#!/usr/bin/env python
+# $Id: Template.py,v 1.16 2006/02/03 21:05:50 tavis_rudd Exp $
+"""Tests of the Template class API
+
+Meta-Data
+================================================================================
+Author: Tavis Rudd <tavis@damnsimple.com>,
+Version: $Revision: 1.16 $
+Start Date: 2001/10/01
+Last Revision Date: $Date: 2006/02/03 21:05:50 $
+"""
+__author__ = "Tavis Rudd <tavis@damnsimple.com>"
+__revision__ = "$Revision: 1.16 $"[11:-2]
+
+
+##################################################
+## DEPENDENCIES ##
+
+import sys
+import types
+import os
+import os.path
+import tempfile
+import shutil
+import unittest_local_copy as unittest
+from Cheetah.Template import Template
+
+##################################################
+## CONSTANTS & GLOBALS ##
+
+majorVer, minorVer = sys.version_info[0], sys.version_info[1]
+versionTuple = (majorVer, minorVer)
+
+try:
+ True,False
+except NameError:
+ True, False = (1==1),(1==0)
+
+##################################################
+## TEST DATA FOR USE IN THE TEMPLATES ##
+
+##################################################
+## TEST BASE CLASSES
+
+class TemplateTest(unittest.TestCase):
+ pass
+
+##################################################
+## TEST CASE CLASSES
+
+class ClassMethods_compile(TemplateTest):
+ """I am using the same Cheetah source for each test to root out clashes
+ caused by the compile caching in Template.compile().
+ """
+
+ def test_basicUsage(self):
+ klass = Template.compile(source='$foo')
+ t = klass(namespaces={'foo':1234})
+ assert str(t)=='1234'
+
+ def test_baseclassArg(self):
+ klass = Template.compile(source='$foo', baseclass=dict)
+ t = klass({'foo':1234})
+ assert str(t)=='1234'
+
+ klass2 = Template.compile(source='$foo', baseclass=klass)
+ t = klass2({'foo':1234})
+ assert str(t)=='1234'
+
+ klass3 = Template.compile(source='#implements dummy\n$bar', baseclass=klass2)
+ t = klass3({'foo':1234})
+ assert str(t)=='1234'
+
+ klass4 = Template.compile(source='$foo', baseclass='dict')
+ t = klass4({'foo':1234})
+ assert str(t)=='1234'
+
+ def test_moduleFileCaching(self):
+ if versionTuple < (2,3):
+ return
+ tmpDir = tempfile.mkdtemp()
+ try:
+ #print tmpDir
+ assert os.path.exists(tmpDir)
+ klass = Template.compile(source='$foo',
+ cacheModuleFilesForTracebacks=True,
+ cacheDirForModuleFiles=tmpDir)
+ mod = sys.modules[klass.__module__]
+ #print mod.__file__
+ assert os.path.exists(mod.__file__)
+ assert os.path.dirname(mod.__file__)==tmpDir
+ finally:
+ shutil.rmtree(tmpDir, True)
+
+ def test_classNameArg(self):
+ klass = Template.compile(source='$foo', className='foo123')
+ assert klass.__name__=='foo123'
+ t = klass(namespaces={'foo':1234})
+ assert str(t)=='1234'
+
+ def test_moduleNameArg(self):
+ klass = Template.compile(source='$foo', moduleName='foo99')
+ mod = sys.modules['foo99']
+ assert klass.__name__=='foo99'
+ t = klass(namespaces={'foo':1234})
+ assert str(t)=='1234'
+
+
+ klass = Template.compile(source='$foo',
+ moduleName='foo1',
+ className='foo2')
+ mod = sys.modules['foo1']
+ assert klass.__name__=='foo2'
+ t = klass(namespaces={'foo':1234})
+ assert str(t)=='1234'
+
+
+ def test_mainMethodNameArg(self):
+ klass = Template.compile(source='$foo',
+ className='foo123',
+ mainMethodName='testMeth')
+ assert klass.__name__=='foo123'
+ t = klass(namespaces={'foo':1234})
+ #print t.generatedClassCode()
+ assert str(t)=='1234'
+ assert t.testMeth()=='1234'
+
+ klass = Template.compile(source='$foo',
+ moduleName='fooXXX',
+ className='foo123',
+ mainMethodName='testMeth',
+ baseclass=dict)
+ assert klass.__name__=='foo123'
+ t = klass({'foo':1234})
+ #print t.generatedClassCode()
+ assert str(t)=='1234'
+ assert t.testMeth()=='1234'
+
+
+
+ def test_moduleGlobalsArg(self):
+ klass = Template.compile(source='$foo',
+ moduleGlobals={'foo':1234})
+ t = klass()
+ assert str(t)=='1234'
+
+ klass2 = Template.compile(source='$foo', baseclass='Test1',
+ moduleGlobals={'Test1':dict})
+ t = klass2({'foo':1234})
+ assert str(t)=='1234'
+
+ klass3 = Template.compile(source='$foo', baseclass='Test1',
+ moduleGlobals={'Test1':dict, 'foo':1234})
+ t = klass3()
+ assert str(t)=='1234'
+
+
+ def test_keepRefToGeneratedCodeArg(self):
+ klass = Template.compile(source='$foo',
+ className='unique58',
+ cacheCompilationResults=False,
+ keepRefToGeneratedCode=False)
+ t = klass(namespaces={'foo':1234})
+ assert str(t)=='1234'
+ assert not t.generatedModuleCode()
+
+
+ klass2 = Template.compile(source='$foo',
+ className='unique58',
+ keepRefToGeneratedCode=True)
+ t = klass2(namespaces={'foo':1234})
+ assert str(t)=='1234'
+ assert t.generatedModuleCode()
+
+ klass3 = Template.compile(source='$foo',
+ className='unique58',
+ keepRefToGeneratedCode=False)
+ t = klass3(namespaces={'foo':1234})
+ assert str(t)=='1234'
+ # still there as this class came from the cache
+ assert t.generatedModuleCode()
+
+
+ def test_compilationCache(self):
+ klass = Template.compile(source='$foo',
+ className='unique111',
+ cacheCompilationResults=False)
+ t = klass(namespaces={'foo':1234})
+ assert str(t)=='1234'
+ assert not klass._CHEETAH_isInCompilationCache
+
+
+ # this time it will place it in the cache
+ klass = Template.compile(source='$foo',
+ className='unique111',
+ cacheCompilationResults=True)
+ t = klass(namespaces={'foo':1234})
+ assert str(t)=='1234'
+ assert klass._CHEETAH_isInCompilationCache
+
+ # by default it will be in the cache
+ klass = Template.compile(source='$foo',
+ className='unique999099')
+ t = klass(namespaces={'foo':1234})
+ assert str(t)=='1234'
+ assert klass._CHEETAH_isInCompilationCache
+
+
+class ClassMethods_subclass(TemplateTest):
+
+ def test_basicUsage(self):
+ klass = Template.compile(source='$foo', baseclass=dict)
+ t = klass({'foo':1234})
+ assert str(t)=='1234'
+
+ klass2 = klass.subclass(source='$foo')
+ t = klass2({'foo':1234})
+ assert str(t)=='1234'
+
+ klass3 = klass2.subclass(source='#implements dummy\n$bar')
+ t = klass3({'foo':1234})
+ assert str(t)=='1234'
+
+
+class Preprocessors(TemplateTest):
+
+ def test_basicUsage1(self):
+ src='''\
+ %set foo = @a
+ $(@foo*10)
+ @a'''
+ src = '\n'.join([ln.strip() for ln in src.splitlines()])
+ preprocessors = {'tokens':'@ %',
+ 'namespaces':{'a':99}
+ }
+ klass = Template.compile(src, preprocessors=preprocessors)
+ assert str(klass())=='990\n99'
+
+ def test_normalizePreprocessorArgVariants(self):
+ src='%set foo = 12\n%%comment\n$(@foo*10)'
+
+ class Settings1: tokens = '@ %'
+ Settings1 = Settings1()
+
+ from Cheetah.Template import TemplatePreprocessor
+ settings = Template._normalizePreprocessorSettings(Settings1)
+ preprocObj = TemplatePreprocessor(settings)
+
+ def preprocFunc(source, file):
+ return '$(12*10)', None
+
+ class TemplateSubclass(Template):
+ pass
+
+ compilerSettings = {'cheetahVarStartToken':'@',
+ 'directiveStartToken':'%',
+ 'commentStartToken':'%%',
+ }
+
+ for arg in ['@ %',
+ {'tokens':'@ %'},
+ {'compilerSettings':compilerSettings},
+ {'compilerSettings':compilerSettings,
+ 'templateInitArgs':{}},
+ {'tokens':'@ %',
+ 'templateAPIClass':TemplateSubclass},
+ Settings1,
+ preprocObj,
+ preprocFunc,
+ ]:
+
+ klass = Template.compile(src, preprocessors=arg)
+ assert str(klass())=='120'
+
+
+ def test_complexUsage(self):
+ src='''\
+ %set foo = @a
+ %def func1: #def func(arg): $arg("***")
+ %% comment
+ $(@foo*10)
+ @func1
+ $func(lambda x:c"--$x--@a")'''
+ src = '\n'.join([ln.strip() for ln in src.splitlines()])
+
+
+ for arg in [{'tokens':'@ %', 'namespaces':{'a':99} },
+ {'tokens':'@ %', 'namespaces':{'a':99} },
+ ]:
+ klass = Template.compile(src, preprocessors=arg)
+ t = klass()
+ assert str(t)=='990\n--***--99'
+
+
+
+ def test_i18n(self):
+ src='''\
+ %i18n: This is a $string that needs translation
+ %i18n id="foo", domain="root": This is a $string that needs translation
+ '''
+ src = '\n'.join([ln.strip() for ln in src.splitlines()])
+ klass = Template.compile(src, preprocessors='@ %', baseclass=dict)
+ t = klass({'string':'bit of text'})
+ #print str(t), repr(str(t))
+ assert str(t)==('This is a bit of text that needs translation\n'*2)[:-1]
+
+
+##################################################
+## if run from the command line ##
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/cobbler/Cheetah/Tests/Test.py b/cobbler/Cheetah/Tests/Test.py
new file mode 100644
index 0000000..9e46a5d
--- /dev/null
+++ b/cobbler/Cheetah/Tests/Test.py
@@ -0,0 +1,70 @@
+#!/usr/bin/env python
+# $Id: Test.py,v 1.44 2006/01/15 20:45:10 tavis_rudd Exp $
+"""Core module of Cheetah's Unit-testing framework
+
+TODO
+================================================================================
+# combo tests
+# negative test cases for expected exceptions
+# black-box vs clear-box testing
+# do some tests that run the Template for long enough to check that the refresh code works
+
+Meta-Data
+================================================================================
+Author: Tavis Rudd <tavis@damnsimple.com>,
+License: This software is released for unlimited distribution under the
+ terms of the MIT license. See the LICENSE file.
+Version: $Revision: 1.44 $
+Start Date: 2001/03/30
+Last Revision Date: $Date: 2006/01/15 20:45:10 $
+"""
+__author__ = "Tavis Rudd <tavis@damnsimple.com>"
+__revision__ = "$Revision: 1.44 $"[11:-2]
+
+
+##################################################
+## DEPENDENCIES ##
+
+import sys
+import unittest_local_copy as unittest
+
+##################################################
+## CONSTANTS & GLOBALS
+
+try:
+ True, False
+except NameError:
+ True, False = (1==1),(1==0)
+
+##################################################
+## TESTS
+
+import SyntaxAndOutput
+import NameMapper
+import Template
+import FileRefresh
+import CheetahWrapper
+
+SyntaxSuite = unittest.findTestCases(SyntaxAndOutput)
+NameMapperSuite = unittest.findTestCases(NameMapper)
+TemplateSuite = unittest.findTestCases(Template)
+FileRefreshSuite = unittest.findTestCases(FileRefresh)
+if not sys.platform.startswith('java'):
+ CheetahWrapperSuite = unittest.findTestCases(CheetahWrapper)
+
+from SyntaxAndOutput import *
+from NameMapper import *
+from Template import *
+from FileRefresh import *
+
+if not sys.platform.startswith('java'):
+ from CheetahWrapper import *
+
+##################################################
+## if run from the command line
+
+if __name__ == '__main__':
+ unittest.main()
+
+
+
diff --git a/cobbler/Cheetah/Tests/__init__.py b/cobbler/Cheetah/Tests/__init__.py
new file mode 100644
index 0000000..792d600
--- /dev/null
+++ b/cobbler/Cheetah/Tests/__init__.py
@@ -0,0 +1 @@
+#
diff --git a/cobbler/Cheetah/Tests/unittest_local_copy.py b/cobbler/Cheetah/Tests/unittest_local_copy.py
new file mode 100644
index 0000000..54061ae
--- /dev/null
+++ b/cobbler/Cheetah/Tests/unittest_local_copy.py
@@ -0,0 +1,977 @@
+#!/usr/bin/env python
+""" This is a hacked version of PyUnit that extends its reporting capabilities
+with optional meta data on the test cases. It also makes it possible to
+separate the standard and error output streams in TextTestRunner.
+
+It's a hack rather than a set of subclasses because a) Steve had used double
+underscore private attributes for some things I needed access to, and b) the
+changes affected so many classes that it was easier just to hack it.
+
+The changes are in the following places:
+TestCase:
+ - minor refactoring of __init__ and __call__ internals
+ - added some attributes and methods for storing and retrieving meta data
+
+_TextTestResult
+ - refactored the stream handling
+ - incorporated all the output code from TextTestRunner
+ - made the output of FAIL and ERROR information more flexible and
+ incorporated the new meta data from TestCase
+ - added a flag called 'explain' to __init__ that controls whether the new '
+ explanation' meta data from TestCase is printed along with tracebacks
+
+TextTestRunner
+ - delegated all output to _TextTestResult
+ - added 'err' and 'explain' to the __init__ signature to match the changes
+ in _TextTestResult
+
+TestProgram
+ - added -e and --explain as flags on the command line
+
+-- Tavis Rudd <tavis@redonions.net> (Sept 28th, 2001)
+
+- _TestTextResult.printErrorList(): print blank line after each traceback
+
+-- Mike Orr <mso@oz.net> (Nov 11, 2002)
+
+TestCase methods copied from unittest in Python 2.3:
+ - .assertAlmostEqual(first, second, places=7, msg=None): to N decimal places.
+ - .failIfAlmostEqual(first, second, places=7, msg=None)
+
+-- Mike Orr (Jan 5, 2004)
+
+
+Below is the original docstring for unittest.
+---------------------------------------------------------------------------
+Python unit testing framework, based on Erich Gamma's JUnit and Kent Beck's
+Smalltalk testing framework.
+
+This module contains the core framework classes that form the basis of
+specific test cases and suites (TestCase, TestSuite etc.), and also a
+text-based utility class for running the tests and reporting the results
+(TextTestRunner).
+
+Simple usage:
+
+ import unittest
+
+ class IntegerArithmenticTestCase(unittest.TestCase):
+ def testAdd(self): ## test method names begin 'test*'
+ self.assertEquals((1 + 2), 3)
+ self.assertEquals(0 + 1, 1)
+ def testMultiply(self);
+ self.assertEquals((0 * 10), 0)
+ self.assertEquals((5 * 8), 40)
+
+ if __name__ == '__main__':
+ unittest.main()
+
+Further information is available in the bundled documentation, and from
+
+ http://pyunit.sourceforge.net/
+
+Copyright (c) 1999, 2000, 2001 Steve Purcell
+This module is free software, and you may redistribute it and/or modify
+it under the same terms as Python itself, so long as this copyright message
+and disclaimer are retained in their original form.
+
+IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
+SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF
+THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+
+THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS,
+AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
+SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+"""
+
+__author__ = "Steve Purcell"
+__email__ = "stephen_purcell at yahoo dot com"
+__revision__ = "$Revision: 1.11 $"[11:-2]
+
+
+##################################################
+## DEPENDENCIES ##
+
+import os
+import re
+import string
+import sys
+import time
+import traceback
+import types
+import pprint
+
+##################################################
+## CONSTANTS & GLOBALS
+
+try:
+ True,False
+except NameError:
+ True, False = (1==1),(1==0)
+
+##############################################################################
+# Test framework core
+##############################################################################
+
+
+class TestResult:
+ """Holder for test result information.
+
+ Test results are automatically managed by the TestCase and TestSuite
+ classes, and do not need to be explicitly manipulated by writers of tests.
+
+ Each instance holds the total number of tests run, and collections of
+ failures and errors that occurred among those test runs. The collections
+ contain tuples of (testcase, exceptioninfo), where exceptioninfo is a
+ tuple of values as returned by sys.exc_info().
+ """
+ def __init__(self):
+ self.failures = []
+ self.errors = []
+ self.testsRun = 0
+ self.shouldStop = 0
+
+ def startTest(self, test):
+ "Called when the given test is about to be run"
+ self.testsRun = self.testsRun + 1
+
+ def stopTest(self, test):
+ "Called when the given test has been run"
+ pass
+
+ def addError(self, test, err):
+ "Called when an error has occurred"
+ self.errors.append((test, err))
+
+ def addFailure(self, test, err):
+ "Called when a failure has occurred"
+ self.failures.append((test, err))
+
+ def addSuccess(self, test):
+ "Called when a test has completed successfully"
+ pass
+
+ def wasSuccessful(self):
+ "Tells whether or not this result was a success"
+ return len(self.failures) == len(self.errors) == 0
+
+ def stop(self):
+ "Indicates that the tests should be aborted"
+ self.shouldStop = 1
+
+ def __repr__(self):
+ return "<%s run=%i errors=%i failures=%i>" % \
+ (self.__class__, self.testsRun, len(self.errors),
+ len(self.failures))
+
+class TestCase:
+ """A class whose instances are single test cases.
+
+ By default, the test code itself should be placed in a method named
+ 'runTest'.
+
+ If the fixture may be used for many test cases, create as
+ many test methods as are needed. When instantiating such a TestCase
+ subclass, specify in the constructor arguments the name of the test method
+ that the instance is to execute.
+
+ Test authors should subclass TestCase for their own tests. Construction
+ and deconstruction of the test's environment ('fixture') can be
+ implemented by overriding the 'setUp' and 'tearDown' methods respectively.
+
+ If it is necessary to override the __init__ method, the base class
+ __init__ method must always be called. It is important that subclasses
+ should not change the signature of their __init__ method, since instances
+ of the classes are instantiated automatically by parts of the framework
+ in order to be run.
+ """
+
+ # This attribute determines which exception will be raised when
+ # the instance's assertion methods fail; test methods raising this
+ # exception will be deemed to have 'failed' rather than 'errored'
+
+ failureException = AssertionError
+
+ # the name of the fixture. Used for displaying meta data about the test
+ name = None
+
+ def __init__(self, methodName='runTest'):
+ """Create an instance of the class that will use the named test
+ method when executed. Raises a ValueError if the instance does
+ not have a method with the specified name.
+ """
+ self._testMethodName = methodName
+ self._setupTestMethod()
+ self._setupMetaData()
+
+ def _setupTestMethod(self):
+ try:
+ self._testMethod = getattr(self, self._testMethodName)
+ except AttributeError:
+ raise ValueError, "no such test method in %s: %s" % \
+ (self.__class__, self._testMethodName)
+
+ ## meta data methods
+
+ def _setupMetaData(self):
+ """Setup the default meta data for the test case:
+
+ - id: self.__class__.__name__ + testMethodName OR self.name + testMethodName
+ - description: 1st line of Class docstring + 1st line of method docstring
+ - explanation: rest of Class docstring + rest of method docstring
+
+ """
+
+
+ testDoc = self._testMethod.__doc__ or '\n'
+ testDocLines = testDoc.splitlines()
+
+ testDescription = testDocLines[0].strip()
+ if len(testDocLines) > 1:
+ testExplanation = '\n'.join(
+ [ln.strip() for ln in testDocLines[1:]]
+ ).strip()
+ else:
+ testExplanation = ''
+
+ fixtureDoc = self.__doc__ or '\n'
+ fixtureDocLines = fixtureDoc.splitlines()
+ fixtureDescription = fixtureDocLines[0].strip()
+ if len(fixtureDocLines) > 1:
+ fixtureExplanation = '\n'.join(
+ [ln.strip() for ln in fixtureDocLines[1:]]
+ ).strip()
+ else:
+ fixtureExplanation = ''
+
+ if not self.name:
+ self.name = self.__class__
+ self._id = "%s.%s" % (self.name, self._testMethodName)
+
+ if not fixtureDescription:
+ self._description = testDescription
+ else:
+ self._description = fixtureDescription + ', ' + testDescription
+
+ if not fixtureExplanation:
+ self._explanation = testExplanation
+ else:
+ self._explanation = ['Fixture Explanation:',
+ '--------------------',
+ fixtureExplanation,
+ '',
+ 'Test Explanation:',
+ '-----------------',
+ testExplanation
+ ]
+ self._explanation = '\n'.join(self._explanation)
+
+ def id(self):
+ return self._id
+
+ def setId(self, id):
+ self._id = id
+
+ def describe(self):
+ """Returns a one-line description of the test, or None if no
+ description has been provided.
+
+ The default implementation of this method returns the first line of
+ the specified test method's docstring.
+ """
+ return self._description
+
+ shortDescription = describe
+
+ def setDescription(self, descr):
+ self._description = descr
+
+ def explain(self):
+ return self._explanation
+
+ def setExplanation(self, expln):
+ self._explanation = expln
+
+ ## core methods
+
+ def setUp(self):
+ "Hook method for setting up the test fixture before exercising it."
+ pass
+
+ def run(self, result=None):
+ return self(result)
+
+ def tearDown(self):
+ "Hook method for deconstructing the test fixture after testing it."
+ pass
+
+ def debug(self):
+ """Run the test without collecting errors in a TestResult"""
+ self.setUp()
+ self._testMethod()
+ self.tearDown()
+
+ ## internal methods
+
+ def defaultTestResult(self):
+ return TestResult()
+
+ def __call__(self, result=None):
+ if result is None:
+ result = self.defaultTestResult()
+
+ result.startTest(self)
+ try:
+ try:
+ self.setUp()
+ except:
+ result.addError(self, self.__exc_info())
+ return
+
+ ok = 0
+ try:
+ self._testMethod()
+ ok = 1
+ except self.failureException, e:
+ result.addFailure(self, self.__exc_info())
+ except:
+ result.addError(self, self.__exc_info())
+ try:
+ self.tearDown()
+ except:
+ result.addError(self, self.__exc_info())
+ ok = 0
+ if ok:
+ result.addSuccess(self)
+ finally:
+ result.stopTest(self)
+
+ return result
+
+ def countTestCases(self):
+ return 1
+
+ def __str__(self):
+ return "%s (%s)" % (self._testMethodName, self.__class__)
+
+ def __repr__(self):
+ return "<%s testMethod=%s>" % \
+ (self.__class__, self._testMethodName)
+
+ def __exc_info(self):
+ """Return a version of sys.exc_info() with the traceback frame
+ minimised; usually the top level of the traceback frame is not
+ needed.
+ """
+ exctype, excvalue, tb = sys.exc_info()
+ if sys.platform[:4] == 'java': ## tracebacks look different in Jython
+ return (exctype, excvalue, tb)
+ newtb = tb.tb_next
+ if newtb is None:
+ return (exctype, excvalue, tb)
+ return (exctype, excvalue, newtb)
+
+ ## methods for use by the test cases
+
+ def fail(self, msg=None):
+ """Fail immediately, with the given message."""
+ raise self.failureException, msg
+
+ def failIf(self, expr, msg=None):
+ "Fail the test if the expression is true."
+ if expr: raise self.failureException, msg
+
+ def failUnless(self, expr, msg=None):
+ """Fail the test unless the expression is true."""
+ if not expr: raise self.failureException, msg
+
+ def failUnlessRaises(self, excClass, callableObj, *args, **kwargs):
+ """Fail unless an exception of class excClass is thrown
+ by callableObj when invoked with arguments args and keyword
+ arguments kwargs. If a different type of exception is
+ thrown, it will not be caught, and the test case will be
+ deemed to have suffered an error, exactly as for an
+ unexpected exception.
+ """
+ try:
+ apply(callableObj, args, kwargs)
+ except excClass:
+ return
+ else:
+ if hasattr(excClass,'__name__'): excName = excClass.__name__
+ else: excName = str(excClass)
+ raise self.failureException, excName
+
+ def failUnlessEqual(self, first, second, msg=None):
+ """Fail if the two objects are unequal as determined by the '!='
+ operator.
+ """
+ if first != second:
+ raise self.failureException, (msg or '%s != %s' % (first, second))
+
+ def failIfEqual(self, first, second, msg=None):
+ """Fail if the two objects are equal as determined by the '=='
+ operator.
+ """
+ if first == second:
+ raise self.failureException, (msg or '%s == %s' % (first, second))
+
+ def failUnlessAlmostEqual(self, first, second, places=7, msg=None):
+ """Fail if the two objects are unequal as determined by their
+ difference rounded to the given number of decimal places
+ (default 7) and comparing to zero.
+
+ Note that decimal places (from zero) is usually not the same
+ as significant digits (measured from the most signficant digit).
+ """
+ if round(second-first, places) != 0:
+ raise self.failureException, \
+ (msg or '%s != %s within %s places' % (`first`, `second`, `places` ))
+
+ def failIfAlmostEqual(self, first, second, places=7, msg=None):
+ """Fail if the two objects are equal as determined by their
+ difference rounded to the given number of decimal places
+ (default 7) and comparing to zero.
+
+ Note that decimal places (from zero) is usually not the same
+ as significant digits (measured from the most signficant digit).
+ """
+ if round(second-first, places) == 0:
+ raise self.failureException, \
+ (msg or '%s == %s within %s places' % (`first`, `second`, `places`))
+
+ ## aliases
+
+ assertEqual = assertEquals = failUnlessEqual
+
+ assertNotEqual = assertNotEquals = failIfEqual
+
+ assertAlmostEqual = assertAlmostEquals = failUnlessAlmostEqual
+
+ assertNotAlmostEqual = assertNotAlmostEquals = failIfAlmostEqual
+
+ assertRaises = failUnlessRaises
+
+ assert_ = failUnless
+
+
+class FunctionTestCase(TestCase):
+ """A test case that wraps a test function.
+
+ This is useful for slipping pre-existing test functions into the
+ PyUnit framework. Optionally, set-up and tidy-up functions can be
+ supplied. As with TestCase, the tidy-up ('tearDown') function will
+ always be called if the set-up ('setUp') function ran successfully.
+ """
+
+ def __init__(self, testFunc, setUp=None, tearDown=None,
+ description=None):
+ TestCase.__init__(self)
+ self.__setUpFunc = setUp
+ self.__tearDownFunc = tearDown
+ self.__testFunc = testFunc
+ self.__description = description
+
+ def setUp(self):
+ if self.__setUpFunc is not None:
+ self.__setUpFunc()
+
+ def tearDown(self):
+ if self.__tearDownFunc is not None:
+ self.__tearDownFunc()
+
+ def runTest(self):
+ self.__testFunc()
+
+ def id(self):
+ return self.__testFunc.__name__
+
+ def __str__(self):
+ return "%s (%s)" % (self.__class__, self.__testFunc.__name__)
+
+ def __repr__(self):
+ return "<%s testFunc=%s>" % (self.__class__, self.__testFunc)
+
+
+ def describe(self):
+ if self.__description is not None: return self.__description
+ doc = self.__testFunc.__doc__
+ return doc and string.strip(string.split(doc, "\n")[0]) or None
+
+ ## aliases
+ shortDescription = describe
+
+class TestSuite:
+ """A test suite is a composite test consisting of a number of TestCases.
+
+ For use, create an instance of TestSuite, then add test case instances.
+ When all tests have been added, the suite can be passed to a test
+ runner, such as TextTestRunner. It will run the individual test cases
+ in the order in which they were added, aggregating the results. When
+ subclassing, do not forget to call the base class constructor.
+ """
+ def __init__(self, tests=(), suiteName=None):
+ self._tests = []
+ self._testMap = {}
+ self.suiteName = suiteName
+ self.addTests(tests)
+
+ def __repr__(self):
+ return "<%s tests=%s>" % (self.__class__, pprint.pformat(self._tests))
+
+ __str__ = __repr__
+
+ def countTestCases(self):
+ cases = 0
+ for test in self._tests:
+ cases = cases + test.countTestCases()
+ return cases
+
+ def addTest(self, test):
+ self._tests.append(test)
+ if isinstance(test, TestSuite) and test.suiteName:
+ name = test.suiteName
+ elif isinstance(test, TestCase):
+ #print test, test._testMethodName
+ name = test._testMethodName
+ else:
+ name = test.__class__.__name__
+ self._testMap[name] = test
+
+ def addTests(self, tests):
+ for test in tests:
+ self.addTest(test)
+
+ def getTestForName(self, name):
+ return self._testMap[name]
+
+ def run(self, result):
+ return self(result)
+
+ def __call__(self, result):
+ for test in self._tests:
+ if result.shouldStop:
+ break
+ test(result)
+ return result
+
+ def debug(self):
+ """Run the tests without collecting errors in a TestResult"""
+ for test in self._tests: test.debug()
+
+
+##############################################################################
+# Text UI
+##############################################################################
+
+class StreamWrapper:
+ def __init__(self, out=sys.stdout, err=sys.stderr):
+ self._streamOut = out
+ self._streamErr = err
+
+ def write(self, txt):
+ self._streamOut.write(txt)
+ self._streamOut.flush()
+
+ def writeln(self, *lines):
+ for line in lines:
+ self.write(line + '\n')
+ if not lines:
+ self.write('\n')
+
+ def writeErr(self, txt):
+ self._streamErr.write(txt)
+
+ def writelnErr(self, *lines):
+ for line in lines:
+ self.writeErr(line + '\n')
+ if not lines:
+ self.writeErr('\n')
+
+
+class _TextTestResult(TestResult, StreamWrapper):
+ _separatorWidth = 70
+ _sep1 = '='
+ _sep2 = '-'
+ _errorSep1 = '*'
+ _errorSep2 = '-'
+ _errorSep3 = ''
+
+ def __init__(self,
+ stream=sys.stdout,
+ errStream=sys.stderr,
+ verbosity=1,
+ explain=False):
+
+ TestResult.__init__(self)
+ StreamWrapper.__init__(self, out=stream, err=errStream)
+
+ self._verbosity = verbosity
+ self._showAll = verbosity > 1
+ self._dots = (verbosity == 1)
+ self._explain = explain
+
+ ## startup and shutdown methods
+
+ def beginTests(self):
+ self._startTime = time.time()
+
+ def endTests(self):
+ self._stopTime = time.time()
+ self._timeTaken = float(self._stopTime - self._startTime)
+
+ def stop(self):
+ self.shouldStop = 1
+
+ ## methods called for each test
+
+ def startTest(self, test):
+ TestResult.startTest(self, test)
+ if self._showAll:
+ self.write("%s (%s)" %( test.id(), test.describe() ) )
+ self.write(" ... ")
+
+ def addSuccess(self, test):
+ TestResult.addSuccess(self, test)
+ if self._showAll:
+ self.writeln("ok")
+ elif self._dots:
+ self.write('.')
+
+ def addError(self, test, err):
+ TestResult.addError(self, test, err)
+ if self._showAll:
+ self.writeln("ERROR")
+ elif self._dots:
+ self.write('E')
+ if err[0] is KeyboardInterrupt:
+ self.stop()
+
+ def addFailure(self, test, err):
+ TestResult.addFailure(self, test, err)
+ if self._showAll:
+ self.writeln("FAIL")
+ elif self._dots:
+ self.write('F')
+
+ ## display methods
+
+ def summarize(self):
+ self.printErrors()
+ self.writeSep2()
+ run = self.testsRun
+ self.writeln("Ran %d test%s in %.3fs" %
+ (run, run == 1 and "" or "s", self._timeTaken))
+ self.writeln()
+ if not self.wasSuccessful():
+ self.writeErr("FAILED (")
+ failed, errored = map(len, (self.failures, self.errors))
+ if failed:
+ self.writeErr("failures=%d" % failed)
+ if errored:
+ if failed: self.writeErr(", ")
+ self.writeErr("errors=%d" % errored)
+ self.writelnErr(")")
+ else:
+ self.writelnErr("OK")
+
+ def writeSep1(self):
+ self.writeln(self._sep1 * self._separatorWidth)
+
+ def writeSep2(self):
+ self.writeln(self._sep2 * self._separatorWidth)
+
+ def writeErrSep1(self):
+ self.writeln(self._errorSep1 * self._separatorWidth)
+
+ def writeErrSep2(self):
+ self.writeln(self._errorSep2 * self._separatorWidth)
+
+ def printErrors(self):
+ if self._dots or self._showAll:
+ self.writeln()
+ self.printErrorList('ERROR', self.errors)
+ self.printErrorList('FAIL', self.failures)
+
+ def printErrorList(self, flavour, errors):
+ for test, err in errors:
+ self.writeErrSep1()
+ self.writelnErr("%s %s (%s)" % (flavour, test.id(), test.describe() ))
+ if self._explain:
+ expln = test.explain()
+ if expln:
+ self.writeErrSep2()
+ self.writeErr( expln )
+ self.writelnErr()
+
+ self.writeErrSep2()
+ for line in apply(traceback.format_exception, err):
+ for l in line.split("\n")[:-1]:
+ self.writelnErr(l)
+ self.writelnErr("")
+
+class TextTestRunner:
+ def __init__(self,
+ stream=sys.stdout,
+ errStream=sys.stderr,
+ verbosity=1,
+ explain=False):
+
+ self._out = stream
+ self._err = errStream
+ self._verbosity = verbosity
+ self._explain = explain
+
+ ## main methods
+
+ def run(self, test):
+ result = self._makeResult()
+ result.beginTests()
+ test( result )
+ result.endTests()
+ result.summarize()
+
+ return result
+
+ ## internal methods
+
+ def _makeResult(self):
+ return _TextTestResult(stream=self._out,
+ errStream=self._err,
+ verbosity=self._verbosity,
+ explain=self._explain,
+ )
+
+##############################################################################
+# Locating and loading tests
+##############################################################################
+
+class TestLoader:
+ """This class is responsible for loading tests according to various
+ criteria and returning them wrapped in a Test
+ """
+ testMethodPrefix = 'test'
+ sortTestMethodsUsing = cmp
+ suiteClass = TestSuite
+
+ def loadTestsFromTestCase(self, testCaseClass):
+ """Return a suite of all tests cases contained in testCaseClass"""
+ return self.suiteClass(tests=map(testCaseClass,
+ self.getTestCaseNames(testCaseClass)),
+ suiteName=testCaseClass.__name__)
+
+ def loadTestsFromModule(self, module):
+ """Return a suite of all tests cases contained in the given module"""
+ tests = []
+ for name in dir(module):
+ obj = getattr(module, name)
+ if type(obj) == types.ClassType and issubclass(obj, TestCase):
+ tests.append(self.loadTestsFromTestCase(obj))
+ return self.suiteClass(tests)
+
+ def loadTestsFromName(self, name, module=None):
+ """Return a suite of all tests cases given a string specifier.
+
+ The name may resolve either to a module, a test case class, a
+ test method within a test case class, or a callable object which
+ returns a TestCase or TestSuite instance.
+
+ The method optionally resolves the names relative to a given module.
+ """
+ parts = string.split(name, '.')
+ if module is None:
+ if not parts:
+ raise ValueError, "incomplete test name: %s" % name
+ else:
+ parts_copy = parts[:]
+ while parts_copy:
+ try:
+ module = __import__(string.join(parts_copy,'.'))
+ break
+ except ImportError:
+ del parts_copy[-1]
+ if not parts_copy: raise
+ parts = parts[1:]
+ obj = module
+ for part in parts:
+ if isinstance(obj, TestSuite):
+ obj = obj.getTestForName(part)
+ else:
+ obj = getattr(obj, part)
+
+ if type(obj) == types.ModuleType:
+ return self.loadTestsFromModule(obj)
+ elif type(obj) == types.ClassType and issubclass(obj, TestCase):
+ return self.loadTestsFromTestCase(obj)
+ elif type(obj) == types.UnboundMethodType:
+ return obj.im_class(obj.__name__)
+ elif isinstance(obj, TestSuite):
+ return obj
+ elif isinstance(obj, TestCase):
+ return obj
+ elif callable(obj):
+ test = obj()
+ if not isinstance(test, TestCase) and \
+ not isinstance(test, TestSuite):
+ raise ValueError, \
+ "calling %s returned %s, not a test" %(obj,test)
+ return test
+ else:
+ raise ValueError, "don't know how to make test from: %s" % obj
+
+ def loadTestsFromNames(self, names, module=None):
+ """Return a suite of all tests cases found using the given sequence
+ of string specifiers. See 'loadTestsFromName()'.
+ """
+ suites = []
+ for name in names:
+ suites.append(self.loadTestsFromName(name, module))
+ return self.suiteClass(suites)
+
+ def getTestCaseNames(self, testCaseClass):
+ """Return a sorted sequence of method names found within testCaseClass.
+ """
+ testFnNames = filter(lambda n,p=self.testMethodPrefix: n[:len(p)] == p,
+ dir(testCaseClass))
+ for baseclass in testCaseClass.__bases__:
+ for testFnName in self.getTestCaseNames(baseclass):
+ if testFnName not in testFnNames: # handle overridden methods
+ testFnNames.append(testFnName)
+ if self.sortTestMethodsUsing:
+ testFnNames.sort(self.sortTestMethodsUsing)
+ return testFnNames
+
+
+
+defaultTestLoader = TestLoader()
+
+
+##############################################################################
+# Patches for old functions: these functions should be considered obsolete
+##############################################################################
+
+def _makeLoader(prefix, sortUsing, suiteClass=None):
+ loader = TestLoader()
+ loader.sortTestMethodsUsing = sortUsing
+ loader.testMethodPrefix = prefix
+ if suiteClass: loader.suiteClass = suiteClass
+ return loader
+
+def getTestCaseNames(testCaseClass, prefix, sortUsing=cmp):
+ return _makeLoader(prefix, sortUsing).getTestCaseNames(testCaseClass)
+
+def makeSuite(testCaseClass, prefix='test', sortUsing=cmp, suiteClass=TestSuite):
+ return _makeLoader(prefix, sortUsing, suiteClass).loadTestsFromTestCase(testCaseClass)
+
+def findTestCases(module, prefix='test', sortUsing=cmp, suiteClass=TestSuite):
+ return _makeLoader(prefix, sortUsing, suiteClass).loadTestsFromModule(module)
+
+##############################################################################
+# Facilities for running tests from the command line
+##############################################################################
+
+class TestProgram:
+ """A command-line program that runs a set of tests; this is primarily
+ for making test modules conveniently executable.
+ """
+ USAGE = """\
+Usage: %(progName)s [options] [test] [...]
+
+Options:
+ -h, --help Show this message
+ -v, --verbose Verbose output
+ -q, --quiet Minimal output
+ -e, --expain Output extra test details if there is a failure or error
+
+Examples:
+ %(progName)s - run default set of tests
+ %(progName)s MyTestSuite - run suite 'MyTestSuite'
+ %(progName)s MyTestSuite.MyTestCase - run suite 'MyTestSuite'
+ %(progName)s MyTestCase.testSomething - run MyTestCase.testSomething
+ %(progName)s MyTestCase - run all 'test*' test methods
+ in MyTestCase
+"""
+ def __init__(self, module='__main__', defaultTest=None,
+ argv=None, testRunner=None, testLoader=defaultTestLoader,
+ testSuite=None):
+ if type(module) == type(''):
+ self.module = __import__(module)
+ for part in string.split(module,'.')[1:]:
+ self.module = getattr(self.module, part)
+ else:
+ self.module = module
+ if argv is None:
+ argv = sys.argv
+ self.test = testSuite
+ self.verbosity = 1
+ self.explain = 0
+ self.defaultTest = defaultTest
+ self.testRunner = testRunner
+ self.testLoader = testLoader
+ self.progName = os.path.basename(argv[0])
+ self.parseArgs(argv)
+ self.runTests()
+
+ def usageExit(self, msg=None):
+ if msg: print msg
+ print self.USAGE % self.__dict__
+ sys.exit(2)
+
+ def parseArgs(self, argv):
+ import getopt
+ try:
+ options, args = getopt.getopt(argv[1:], 'hHvqer',
+ ['help','verbose','quiet','explain', 'raise'])
+ for opt, value in options:
+ if opt in ('-h','-H','--help'):
+ self.usageExit()
+ if opt in ('-q','--quiet'):
+ self.verbosity = 0
+ if opt in ('-v','--verbose'):
+ self.verbosity = 2
+ if opt in ('-e','--explain'):
+ self.explain = True
+ if len(args) == 0 and self.defaultTest is None and self.test is None:
+ self.test = self.testLoader.loadTestsFromModule(self.module)
+ return
+ if len(args) > 0:
+ self.testNames = args
+ else:
+ self.testNames = (self.defaultTest,)
+ self.createTests()
+ except getopt.error, msg:
+ self.usageExit(msg)
+
+ def createTests(self):
+ if self.test == None:
+ self.test = self.testLoader.loadTestsFromNames(self.testNames,
+ self.module)
+
+ def runTests(self):
+ if self.testRunner is None:
+ self.testRunner = TextTestRunner(verbosity=self.verbosity,
+ explain=self.explain)
+ result = self.testRunner.run(self.test)
+ self._cleanupAfterRunningTests()
+ sys.exit(not result.wasSuccessful())
+
+ def _cleanupAfterRunningTests(self):
+ """A hook method that is called immediately prior to calling
+ sys.exit(not result.wasSuccessful()) in self.runTests().
+ """
+ pass
+
+main = TestProgram
+
+
+##############################################################################
+# Executing this module from the command line
+##############################################################################
+
+if __name__ == "__main__":
+ main(module=None)
+
+# vim: shiftwidth=4 tabstop=4 expandtab