summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Madden <jamadden@gmail.com>2017-08-14 11:53:34 -0500
committerJason Madden <jamadden@gmail.com>2017-08-14 11:53:34 -0500
commit67c529190c57b5ff47de51694a21a173223da0de (patch)
treec5fad7d7b613ba4ce6cf3dac3d0e59873cf3ac7c
parent7cea1b2eaf656edd2da0c561a225ffeec26067f6 (diff)
downloadzope-pagetemplate-coverage.tar.gz
100% test coveragecoverage
Fixes #9 Two potentially important changes: - Removed unused code in the tests/ package. If other packages were using it, they will need updates. (They shouldn't be, but that's happened before.) - Changed TraversableModuleImporter to catch ImportError instead of KeyError. There's no way the underlying SimpleModuleImporter can raise a KeyError anymore. I think this is the desired semantic but I am not 100% sure.
-rw-r--r--CHANGES.rst9
-rw-r--r--src/zope/pagetemplate/engine.py25
-rw-r--r--src/zope/pagetemplate/pagetemplate.py22
-rw-r--r--src/zope/pagetemplate/pagetemplatefile.py26
-rw-r--r--src/zope/pagetemplate/tests/batch.py107
-rw-r--r--src/zope/pagetemplate/tests/test_basictemplate.py194
-rw-r--r--src/zope/pagetemplate/tests/test_engine.py140
-rw-r--r--src/zope/pagetemplate/tests/test_htmltests.py25
-rw-r--r--src/zope/pagetemplate/tests/test_ptfile.py98
-rw-r--r--src/zope/pagetemplate/tests/util.py71
-rw-r--r--tox.ini2
11 files changed, 419 insertions, 300 deletions
diff --git a/CHANGES.rst b/CHANGES.rst
index 710108f..4d529ba 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -9,6 +9,15 @@
- Drop support for Python 2.6, 3.2 and 3.3.
+- Certain internal test support objects in the ``tests`` package were
+ removed or modified.
+
+- The ``TraversableModuleImporter`` properly turns ``ImportError``
+ into ``TraversalError``. Previously it was catching ``KeyError``,
+ which cannot be raised.
+
+- Reach 100% code coverage and maintain it through automated testing.
+
4.2.1 (2015-06-06)
==================
diff --git a/src/zope/pagetemplate/engine.py b/src/zope/pagetemplate/engine.py
index f3687d7..cea32d7 100644
--- a/src/zope/pagetemplate/engine.py
+++ b/src/zope/pagetemplate/engine.py
@@ -33,7 +33,7 @@ try:
from zope.untrustedpython import rcompile
from zope.untrustedpython.builtins import SafeBuiltins
HAVE_UNTRUSTED = True
-except ImportError:
+except ImportError: # pragma: no cover
HAVE_UNTRUSTED = False
# PyPy doesn't support assigning to '__builtins__', even when
@@ -41,7 +41,7 @@ except ImportError:
# so don't try to use it. It won't work.
if HAVE_UNTRUSTED:
import platform
- if platform.python_implementation() == 'PyPy':
+ if platform.python_implementation() == 'PyPy': # pragma: no cover
HAVE_UNTRUSTED = False
del rcompile
del SafeBuiltins
@@ -118,10 +118,15 @@ class ZopePythonExpr(PythonExpr):
def _compile(self, text, filename):
return rcompile.compile(text, filename, 'eval')
+def _get_iinterpreter():
+ from zope.app.interpreter.interfaces import IInterpreter
+ return IInterpreter # pragma: no cover
class ZopeContextBase(Context):
"""Base class for both trusted and untrusted evaluation contexts."""
+ request = None
+
def translate(self, msgid, domain=None, mapping=None, default=None):
return translate(msgid, domain, mapping,
context=self.request, default=default)
@@ -131,24 +136,24 @@ class ZopeContextBase(Context):
def evaluateCode(self, lang, code):
if not self.evaluateInlineCode:
raise InlineCodeError(
- _('Inline Code Evaluation is deactivated, which means that '
- 'you cannot have inline code snippets in your Page '
- 'Template. Activate Inline Code Evaluation and try again.'))
+ _('Inline Code Evaluation is deactivated, which means that '
+ 'you cannot have inline code snippets in your Page '
+ 'Template. Activate Inline Code Evaluation and try again.'))
# TODO This is only needed when self.evaluateInlineCode is true,
# so should only be needed for zope.app.pythonpage.
- from zope.app.interpreter.interfaces import IInterpreter
+ IInterpreter = _get_iinterpreter()
interpreter = component.queryUtility(IInterpreter, lang)
if interpreter is None:
error = _('No interpreter named "${lang_name}" was found.',
mapping={'lang_name': lang})
raise InlineCodeError(error)
- globals = self.vars.copy()
- result = interpreter.evaluateRawCode(code, globals)
+ globs = self.vars.copy()
+ result = interpreter.evaluateRawCode(code, globs)
# Add possibly new global variables.
old_names = self.vars.keys()
- for name, value in globals.items():
+ for name, value in globs.items():
if name not in old_names:
self.setGlobal(name, value)
return result
@@ -466,7 +471,7 @@ class TraversableModuleImporter(SimpleModuleImporter):
def traverse(self, name, further_path):
try:
return self[name]
- except KeyError:
+ except ImportError:
raise TraversalError(self, name)
diff --git a/src/zope/pagetemplate/pagetemplate.py b/src/zope/pagetemplate/pagetemplate.py
index b6bf8c2..9c05c89 100644
--- a/src/zope/pagetemplate/pagetemplate.py
+++ b/src/zope/pagetemplate/pagetemplate.py
@@ -30,8 +30,6 @@ from zope.pagetemplate.interfaces import IPageTemplateProgram
from zope.interface import implementer
from zope.interface import provider
-from six import u as _u
-
_default_options = {}
@@ -46,7 +44,7 @@ class StringIO(list):
self.append(value)
def getvalue(self):
- return _u('').join(self)
+ return u''.join(self)
@implementer(IPageTemplateSubclassing)
@@ -87,12 +85,11 @@ class PageTemplate(object):
_v_program = None
_text = ''
+ @property
def macros(self):
self._cook_check()
return self._v_macros
- macros = property(macros)
-
def pt_edit(self, text, content_type):
if content_type:
self.content_type = str(content_type)
@@ -105,7 +102,7 @@ class PageTemplate(object):
'options': options,
'args': args,
'nothing': None,
- }
+ }
rval.update(self.pt_getEngine().getBaseNames())
return rval
@@ -145,15 +142,18 @@ class PageTemplate(object):
if check_macro_expansion:
try:
self.pt_render(namespace, source=1)
- except:
+ except Exception:
return ('Macro expansion failed', '%s: %s' % sys.exc_info()[:2])
def _convert(self, string, text):
"""Adjust the string type to the type of text"""
- if isinstance(text, six.binary_type):
+ if isinstance(text, six.binary_type) and not isinstance(string, six.binary_type):
return string.encode('utf-8')
- else:
- return string
+
+ if isinstance(text, six.text_type) and not isinstance(string, six.text_type):
+ return string.decode('utf-8')
+
+ return string
def write(self, text):
# We accept both, since the text can either come from a file (and the
@@ -252,7 +252,7 @@ class PageTemplateEngine(object):
self.program = program
def __call__(self, context, macros, **options):
- output = StringIO(_u(''))
+ output = StringIO(u'')
interpreter = TALInterpreter(
self.program, macros, context,
stream=output, **options
diff --git a/src/zope/pagetemplate/pagetemplatefile.py b/src/zope/pagetemplate/pagetemplatefile.py
index cee1c07..4cf0bf3 100644
--- a/src/zope/pagetemplate/pagetemplatefile.py
+++ b/src/zope/pagetemplate/pagetemplatefile.py
@@ -25,6 +25,8 @@ import logging
from zope.pagetemplate.pagetemplate import PageTemplate
+logger = logging.getLogger(__name__)
+
DEFAULT_ENCODING = "utf-8"
meta_pattern = re.compile(
@@ -40,10 +42,7 @@ class PageTemplateFile(PageTemplate):
"Zope wrapper for filesystem Page Template using TAL, TALES, and METAL"
_v_last_read = 0
-
- #_error_start = b'<!-- Page Template Diagnostics'
- #_error_end = b'-->'
- #_newline = b'\n'
+ _v_debug = __debug__
def __init__(self, filename, _prefix=None):
path = self.get_path_from_prefix(_prefix)
@@ -75,21 +74,18 @@ class PageTemplateFile(PageTemplate):
def _read_file(self):
__traceback_info__ = self.filename
- f = open(self.filename, "rb")
- try:
+ with open(self.filename, "rb") as f:
text = f.read(XML_PREFIX_MAX_LENGTH)
- except:
- f.close()
- raise
- type_ = sniff_type(text)
- text += f.read()
+ type_ = sniff_type(text)
+ text += f.read()
+
if type_ != "text/xml":
text, type_ = self._prepare_html(text)
- f.close()
+
return text, type_
def _cook_check(self):
- if self._v_last_read and not __debug__:
+ if self._v_last_read and not self._v_debug:
return
__traceback_info__ = self.filename
try:
@@ -102,8 +98,8 @@ class PageTemplateFile(PageTemplate):
self.pt_edit(text, type_)
assert self._v_cooked
if self._v_errors:
- logging.error('PageTemplateFile: Error in template %s: %s',
- self.filename, '\n'.join(self._v_errors))
+ logger.error('PageTemplateFile: Error in template %s: %s',
+ self.filename, '\n'.join(self._v_errors))
return
self._v_last_read = mtime
diff --git a/src/zope/pagetemplate/tests/batch.py b/src/zope/pagetemplate/tests/batch.py
index 45f3ad9..5d193bd 100644
--- a/src/zope/pagetemplate/tests/batch.py
+++ b/src/zope/pagetemplate/tests/batch.py
@@ -20,97 +20,52 @@ class batch(object):
def __init__(self, sequence, size, start=0, end=0,
orphan=3, overlap=0):
- start=start+1
+ start = start + 1
- start,end,sz=opt(start,end,size,orphan,sequence)
+ start, end, sz = opt(start, end, size, orphan, sequence)
- self._last=end-1
- self._first=start-1
+ self._last = end - 1
+ self._first = start - 1
- self._sequence=sequence
- self._size=size
- self._start=start
- self._end=end
- self._orphan=orphan
- self._overlap=overlap
+ self._sequence = sequence
+ self._size = size
+ self._start = start
+ self._end = end
+ self._orphan = orphan
+ self._overlap = overlap
- def previous_sequence(self): return self._first
-
- def previous_sequence_end_number(self):
- start,end,spam=opt(0, self._start-1+self._overlap,
- self._size, self._orphan, self._sequence)
- return end
-
- def previous_sequence_start_number(self):
- start,end,spam=opt(0, self._start-1+self._overlap,
- self._size, self._orphan, self._sequence)
- return start
-
- def previous_sequence_end_item(self):
- start,end,spam=opt(0, self._start-1+self._overlap,
- self._size, self._orphan, self._sequence)
- return self._sequence[end-1]
-
- def previous_sequence_start_item(self):
- start,end,spam=opt(0, self._start-1+self._overlap,
- self._size, self._orphan, self._sequence)
- return self._sequence[start-1]
-
- def next_sequence_end_number(self):
- start,end,spam=opt(self._end+1-self._overlap, 0,
- self._size, self._orphan, self._sequence)
- return end
-
- def next_sequence_start_number(self):
- start,end,spam=opt(self._end+1-self._overlap, 0,
- self._size, self._orphan, self._sequence)
- return start
+ def previous_sequence(self):
+ return self._first
def next_sequence_end_item(self):
- start,end,spam=opt(self._end+1-self._overlap, 0,
- self._size, self._orphan, self._sequence)
+ _start, end, _spam = opt(self._end+1-self._overlap, 0,
+ self._size, self._orphan, self._sequence)
return self._sequence[end-1]
def next_sequence_start_item(self):
- start,end,spam=opt(self._end+1-self._overlap, 0,
- self._size, self._orphan, self._sequence)
+ start, _end, _spam = opt(self._end+1-self._overlap, 0,
+ self._size, self._orphan, self._sequence)
return self._sequence[start-1]
-
def next_sequence(self):
- try: self._sequence[self._end]
- except IndexError: return 0
- else: return 1
+ return self._end < len(self._sequence)
+ # try: self._sequence[self._end]
+ # except IndexError: return 0
+ # else: return 1
def __getitem__(self, index):
- if index > self._last: raise IndexError(index)
- return self._sequence[index+self._first]
+ if index > self._last:
+ raise IndexError(index)
+ return self._sequence[index + self._first]
-def opt(start,end,size,orphan,sequence):
- if size < 1:
- if start > 0 and end > 0 and end >= start:
- size=end+1-start
- else: size=7
+def opt(start, end, size, orphan, sequence):
+ assert size >= 1
+ assert start > 0
- if start > 0:
+ start = len(sequence) if start - 1 >= len(sequence) else start
+ assert end <= 0
+ end = start + size - 1
- try: sequence[start-1]
- except: start=len(sequence)
+ assert end + orphan - 1 < len(sequence)
- if end > 0:
- if end < start: end=start
- else:
- end=start+size-1
- try: sequence[end+orphan-1]
- except: end=len(sequence)
- elif end > 0:
- try: sequence[end-1]
- except: end=len(sequence)
- start=end+1-size
- if start - 1 < orphan: start=1
- else:
- start=1
- end=start+size-1
- try: sequence[end+orphan-1]
- except: end=len(sequence)
- return start,end,size
+ return start, end, size
diff --git a/src/zope/pagetemplate/tests/test_basictemplate.py b/src/zope/pagetemplate/tests/test_basictemplate.py
index 430a901..aa275a4 100644
--- a/src/zope/pagetemplate/tests/test_basictemplate.py
+++ b/src/zope/pagetemplate/tests/test_basictemplate.py
@@ -15,8 +15,6 @@
"""
import unittest
-from six import u as _u
-
from zope.pagetemplate.tests import util
import zope.pagetemplate.pagetemplate
import zope.component.testing
@@ -32,26 +30,25 @@ class BasicTemplateTests(unittest.TestCase):
def test_if_in_var(self):
# DTML test 1: if, in, and var:
- pass # for unittest
- """
- %(comment)[ blah %(comment)]
- <html><head><title>Test of documentation templates</title></head>
- <body>
- %(if args)[
- <dl><dt>The arguments to this test program were:<p>
- <dd>
- <ul>
- %(in args)[
- <li>Argument number %(num)d was %(arg)s
- %(in args)]
- </ul></dl><p>
- %(if args)]
- %(else args)[
- No arguments were given.<p>
- %(else args)]
- And thats da trooth.
- </body></html>
- """
+ # """
+ # %(comment)[ blah %(comment)]
+ # <html><head><title>Test of documentation templates</title></head>
+ # <body>
+ # %(if args)[
+ # <dl><dt>The arguments to this test program were:<p>
+ # <dd>
+ # <ul>
+ # %(in args)[
+ # <li>Argument number %(num)d was %(arg)s
+ # %(in args)]
+ # </ul></dl><p>
+ # %(if args)]
+ # %(else args)[
+ # No arguments were given.<p>
+ # %(else args)]
+ # And thats da trooth.
+ # </body></html>
+ # """
tal = util.read_input('dtml1.html')
self.t.write(tal)
@@ -88,11 +85,11 @@ class BasicTemplateTests(unittest.TestCase):
from zope.component import provideUtility
class DummyProgram(object):
- def __init__(*args):
+ def __init__(self, *args):
self.args = args
- def __call__(*args, **kwargs):
- return self.args, args, kwargs
+ def __call__(self, *args, **kwargs):
+ return self.args, (self,) + args, kwargs
class DummyEngine(object):
@staticmethod
@@ -102,19 +99,19 @@ class BasicTemplateTests(unittest.TestCase):
provideUtility(DummyEngine, IPageTemplateEngine)
self.t._cook()
- self.assertTrue(isinstance(self.t._v_program, DummyProgram))
+ self.assertIsInstance(self.t._v_program, DummyProgram)
self.assertEqual(self.t._v_macros, "macros")
# "Render" and unpack arguments passed for verification
- ((cls, source_file, text, engine, content_type),
- (program, context, macros),
- options) = \
- self.t.pt_render({})
+ ((source_file, text, _engine, content_type),
+ (program, _context, macros),
+ options) = self.t.pt_render({})
self.assertEqual(source_file, None)
self.assertEqual(text, 'foo')
self.assertEqual(content_type, 'text/html')
- self.assertTrue(isinstance(program, DummyProgram))
+ self.assertEqual(macros, 'macros')
+ self.assertIsInstance(program, DummyProgram)
self.assertEqual(options, {
'tal': True,
'showtal': False,
@@ -124,43 +121,43 @@ class BasicTemplateTests(unittest.TestCase):
def test_batches_and_formatting(self):
# DTML test 3: batches and formatting:
- pass # for unittest
- """
- <html><head><title>Test of documentation templates</title></head>
- <body>
- <!--#if args-->
- The arguments were:
- <!--#in args size=size end=end-->
- <!--#if previous-sequence-->
- (<!--#var previous-sequence-start-arg-->-
- <!--#var previous-sequence-end-arg-->)
- <!--#/if previous-sequence-->
- <!--#if sequence-start-->
- <dl>
- <!--#/if sequence-start-->
- <dt><!--#var sequence-arg-->.</dt>
- <dd>Argument <!--#var num fmt=d--> was <!--#var arg--></dd>
- <!--#if next-sequence-->
- (<!--#var next-sequence-start-arg-->-
- <!--#var next-sequence-end-arg-->)
- <!--#/if next-sequence-->
- <!--#/in args-->
- </dl>
- <!--#else args-->
- No arguments were given.<p>
- <!--#/if args-->
- And I\'m 100% sure!
- </body></html>
- """
+ # """
+ # <html><head><title>Test of documentation templates</title></head>
+ # <body>
+ # <!--#if args-->
+ # The arguments were:
+ # <!--#in args size=size end=end-->
+ # <!--#if previous-sequence-->
+ # (<!--#var previous-sequence-start-arg-->-
+ # <!--#var previous-sequence-end-arg-->)
+ # <!--#/if previous-sequence-->
+ # <!--#if sequence-start-->
+ # <dl>
+ # <!--#/if sequence-start-->
+ # <dt><!--#var sequence-arg-->.</dt>
+ # <dd>Argument <!--#var num fmt=d--> was <!--#var arg--></dd>
+ # <!--#if next-sequence-->
+ # (<!--#var next-sequence-start-arg-->-
+ # <!--#var next-sequence-end-arg-->)
+ # <!--#/if next-sequence-->
+ # <!--#/in args-->
+ # </dl>
+ # <!--#else args-->
+ # No arguments were given.<p>
+ # <!--#/if args-->
+ # And I\'m 100% sure!
+ # </body></html>
+ # """
tal = util.read_input('dtml3.html')
self.t.write(tal)
- aa = util.argv(('one', 'two', 'three', 'four', 'five',
- 'six', 'seven', 'eight', 'nine', 'ten',
- 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen',
- 'sixteen', 'seventeen', 'eighteen', 'nineteen',
- 'twenty',
- ))
+ aa = util.argv((
+ 'one', 'two', 'three', 'four', 'five',
+ 'six', 'seven', 'eight', 'nine', 'ten',
+ 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen',
+ 'sixteen', 'seventeen', 'eighteen', 'nineteen',
+ 'twenty',
+ ))
from zope.pagetemplate.tests import batch
o = self.t(content=aa, batch=batch.batch(aa.args, 5))
@@ -206,7 +203,7 @@ class BasicTemplateTests(unittest.TestCase):
self.t()
def test_unicode_html(self):
- text = _u('<p>\xe4\xf6\xfc\xdf</p>')
+ text = u'<p>\xe4\xf6\xfc\xdf</p>'
# test with HTML parser
self.t.pt_edit(text, 'text/html')
@@ -216,8 +213,65 @@ class BasicTemplateTests(unittest.TestCase):
self.t.pt_edit(text, 'text/xml')
self.assertEqual(self.t().strip(), text)
-def test_suite():
- return unittest.makeSuite(BasicTemplateTests)
+ def test_edit_with_read(self):
+ from io import BytesIO
+ self.t.pt_edit(BytesIO(b"<html/>"), None)
+ self.assertEqual(self.t._text, b'<html/>')
+
+ def test_errors(self):
+ self.t._v_cooked = True
+ self.t._v_errors = 1
+ e = self.t.pt_errors(None)
+ self.assertEqual(e, 1)
+
+ self.t._v_errors = ()
+ e = self.t.pt_errors(None)
+ self.assertEqual(e[0], 'Macro expansion failed')
+
+ def test_convert(self):
+ string = u'binary'
+ text = b'binary'
+ self.assertEqual(text, self.t._convert(string, text))
+
+ def test_write_error(self):
+ self.t.write(self.t._error_start + 'stuff' + self.t._error_end + self.t._newline)
+ self.assertEqual(self.t._text, '')
+
+ def test_read_no_expand(self):
+ self.t.expand = False
+ self.t._text = self
+ self.t._v_cooked = True
+
+ self.assertIs(self.t.read(), self)
+
+ def test_read_error_expand(self):
+ self.t.expand = True
+ self.t._text = ''
+ self.t._v_cooked = True
+ text = self.t.read()
+ self.assertIn(self.t._error_start, text)
+ self.assertIn("Macro expansion failed", text)
+
+
+ def test_macros(self):
+ self.assertEqual(self.t.macros, {})
+
+
+class TestPageTemplateTracebackSupplement(unittest.TestCase):
+
+ def test_errors_old_style(self):
+ class PT(object):
+ def pt_errors(self, ns):
+ return (ns,)
+
+ pts = zope.pagetemplate.pagetemplate.PageTemplateTracebackSupplement(PT(), 'ns')
+
+ self.assertEqual(pts.warnings, ['ns'])
+
+ def test_errors_none(self):
+ class PT(object):
+ def pt_errors(self, ns, check_macro_expansion=False):
+ return None
-if __name__ == '__main__':
- unittest.TextTestRunner().run(test_suite())
+ pts = zope.pagetemplate.pagetemplate.PageTemplateTracebackSupplement(PT(), 'ns')
+ self.assertEqual(pts.warnings, [])
diff --git a/src/zope/pagetemplate/tests/test_engine.py b/src/zope/pagetemplate/tests/test_engine.py
index ab3b5ca..ab61408 100644
--- a/src/zope/pagetemplate/tests/test_engine.py
+++ b/src/zope/pagetemplate/tests/test_engine.py
@@ -18,34 +18,29 @@ import re
import unittest
import zope.pagetemplate.engine
from zope.testing.renormalizing import RENormalizing
+from zope.component.testing import PlacelessSetup
+class EngineTests(PlacelessSetup,
+ unittest.TestCase):
-class DummyNamespace(object):
-
- def __init__(self, context):
- self.context = context
-
-class EngineTests(unittest.TestCase):
-
- def setUp(self):
- from zope.component.testing import setUp
- setUp()
-
- def tearDown(self):
- from zope.component.testing import tearDown
- tearDown()
+ def _makeOne(self):
+ return zope.pagetemplate.engine._Engine()
def test_function_namespaces_return_secured_proxies(self):
# See https://bugs.launchpad.net/zope3/+bug/98323
- from zope.component import provideAdapter
- from zope.traversing.interfaces import IPathAdapter
- from zope.pagetemplate.engine import _Engine
from zope.proxy import isProxy
- provideAdapter(DummyNamespace, (None,), IPathAdapter, name='test')
- engine = _Engine()
+ engine = self._makeOne()
namespace = engine.getFunctionNamespace('test')
self.assertTrue(isProxy(namespace))
+ def test_getContext_namespace(self):
+ engine = self._makeOne()
+ ctx = engine.getContext({'a': 1}, b=2, request=3, context=4)
+ self.assertEqual(ctx.getValue('a'), 1)
+ self.assertEqual(ctx.getValue('b'), 2)
+ self.assertEqual(ctx.getValue('request'), 3)
+ self.assertEqual(ctx.getValue('context'), 4)
+
class DummyEngine(object):
def getTypes(self):
@@ -71,6 +66,8 @@ class ZopePythonExprTests(unittest.TestCase):
DummyEngine())
self.assertEqual(expr(DummyContext()), 'sys')
+ @unittest.skipUnless(zope.pagetemplate.engine.HAVE_UNTRUSTED,
+ "Needs untrusted")
def test_forbidden_module_name(self):
from zope.pagetemplate.engine import ZopePythonExpr
from zope.security.interfaces import Forbidden
@@ -78,12 +75,110 @@ class ZopePythonExprTests(unittest.TestCase):
DummyEngine())
self.assertRaises(Forbidden, expr, DummyContext())
+ @unittest.skipUnless(zope.pagetemplate.engine.HAVE_UNTRUSTED,
+ "Needs untrusted")
def test_disallowed_builtin(self):
from zope.pagetemplate.engine import ZopePythonExpr
expr = ZopePythonExpr('python', 'open("x", "w")', DummyEngine())
self.assertRaises(NameError, expr, DummyContext())
+class TestZopeContext(PlacelessSetup,
+ unittest.TestCase):
+
+ def _makeOne(self):
+ return zope.pagetemplate.engine.ZopeContext(None, {})
+
+ def test_translate(self):
+ ctx = self._makeOne()
+ self.assertEqual(ctx.translate('msgid'), 'msgid')
+
+ def test_evaluate_error(self):
+ ctx = self._makeOne()
+ with self.assertRaisesRegexp(zope.pagetemplate.engine.InlineCodeError,
+ "Inline Code Evaluation is deactivated"):
+ ctx.evaluateCode('lang', 'code')
+
+ def test_evaluate_interpreter_not_importable(self):
+ ctx = self._makeOne()
+ ctx.evaluateInlineCode = True
+ with self.assertRaises(ImportError):
+ ctx.evaluateCode('lang', 'code')
+
+ def test_evaluate_interpreter_not_found(self):
+ get = zope.pagetemplate.engine._get_iinterpreter
+ from zope import interface
+ class IInterpreter(interface.Interface):
+ pass
+ def mock_get():
+ return IInterpreter
+
+ ctx = self._makeOne()
+ ctx.evaluateInlineCode = True
+ zope.pagetemplate.engine._get_iinterpreter = mock_get
+ try:
+ with self.assertRaisesRegexp(zope.pagetemplate.engine.InlineCodeError,
+ "No interpreter named"):
+ ctx.evaluateCode('lang', 'code')
+ finally:
+ zope.pagetemplate.engine._get_iinterpreter = get
+
+ def test_evaluate_interpreter_found(self):
+ get = zope.pagetemplate.engine._get_iinterpreter
+ from zope import interface
+ from zope import component
+ class IInterpreter(interface.Interface):
+ pass
+ def mock_get():
+ return IInterpreter
+
+ @interface.implementer(IInterpreter)
+ class Interpreter(object):
+ def evaluateRawCode(self, code, globs):
+ globs['new'] = code
+ return 42
+
+ component.provideUtility(Interpreter(), name='lang')
+
+ ctx = self._makeOne()
+ ctx.evaluateInlineCode = True
+ zope.pagetemplate.engine._get_iinterpreter = mock_get
+ try:
+ result = ctx.evaluateCode('lang', 'code')
+ finally:
+ zope.pagetemplate.engine._get_iinterpreter = get
+
+ self.assertEqual(result, 42)
+ self.assertEqual('code', ctx.getValue('new'))
+
+
+class TestTraversableModuleImporter(unittest.TestCase):
+
+ def test_traverse_fails(self):
+ from zope.traversing.interfaces import TraversalError
+
+ tmi = zope.pagetemplate.engine.TraversableModuleImporter()
+ with self.assertRaises(TraversalError):
+ tmi.traverse('zope.cannot exist', ())
+
+ with self.assertRaises(TraversalError):
+ tmi.traverse('zope.pagetemplate.engine.DNE', ())
+
+
+ with self.assertRaises(TraversalError):
+ tmi.traverse('pickle.no_sub_module', ())
+
+
+class TestAppPT(unittest.TestCase):
+
+ def test_apppt_engine(self):
+ self.assertIs(zope.pagetemplate.engine.AppPT().pt_getEngine(),
+ zope.pagetemplate.engine.Engine)
+
+ def test_trustedapppt_engine(self):
+ self.assertIs(zope.pagetemplate.engine.TrustedAppPT().pt_getEngine(),
+ zope.pagetemplate.engine.TrustedEngine)
+
def test_suite():
@@ -96,15 +191,12 @@ def test_suite():
(re.compile(r"<class 'list'>"), "<type 'list'>"),
# PyPy/pure-Python implementation
(re.compile(r"<class 'zope.security.proxy.ProxyPy'>"),
- "<type 'zope.security._proxy._Proxy'>"),
+ "<type 'zope.security._proxy._Proxy'>"),
])
- suite = unittest.TestSuite()
+ suite = unittest.defaultTestLoader.loadTestsFromName(__name__)
suite.addTest(doctest.DocTestSuite('zope.pagetemplate.engine',
checker=checker))
- suite.addTest(unittest.makeSuite(EngineTests))
- if zope.pagetemplate.engine.HAVE_UNTRUSTED:
- suite.addTest(unittest.makeSuite(ZopePythonExprTests))
return suite
diff --git a/src/zope/pagetemplate/tests/test_htmltests.py b/src/zope/pagetemplate/tests/test_htmltests.py
index c6abd81..1550634 100644
--- a/src/zope/pagetemplate/tests/test_htmltests.py
+++ b/src/zope/pagetemplate/tests/test_htmltests.py
@@ -31,15 +31,17 @@ class HTMLTests(unittest.TestCase):
def getProducts(self):
return [
- {'description': 'This is the tee for those who LOVE Zope. '
- 'Show your heart on your tee.',
- 'price': 12.99, 'image': 'smlatee.jpg'
+ {
+ 'description': ('This is the tee for those who LOVE Zope. '
+ 'Show your heart on your tee.'),
+ 'price': 12.99, 'image': 'smlatee.jpg'
},
- {'description': 'This is the tee for Jim Fulton. '
- 'He\'s the Zope Pope!',
- 'price': 11.99, 'image': 'smpztee.jpg'
+ {
+ 'description': ('This is the tee for Jim Fulton. '
+ 'He\'s the Zope Pope!'),
+ 'price': 11.99, 'image': 'smpztee.jpg'
},
- ]
+ ]
def test_1(self):
laf = self.folder.laf
@@ -53,7 +55,7 @@ class HTMLTests(unittest.TestCase):
t = self.folder.t
t.write(util.read_input('teeshop2.html'))
expect = util.read_output('teeshop2.html')
- out = t(laf = self.folder.laf, getProducts = self.getProducts)
+ out = t(laf=self.folder.laf, getProducts=self.getProducts)
util.check_html(expect, out)
@@ -63,7 +65,7 @@ class HTMLTests(unittest.TestCase):
t = self.folder.t
t.write(util.read_input('teeshop1.html'))
expect = util.read_output('teeshop1.html')
- out = t(laf = self.folder.laf, getProducts = self.getProducts)
+ out = t(laf=self.folder.laf, getProducts=self.getProducts)
util.check_html(expect, out)
def test_SimpleLoop(self):
@@ -152,7 +154,4 @@ class HTMLTests(unittest.TestCase):
self.assertFalse(errors)
def test_suite():
- return unittest.makeSuite(HTMLTests)
-
-if __name__=='__main__':
- unittest.TextTestRunner().run(test_suite())
+ return unittest.defaultTestLoader.loadTestsFromName(__name__)
diff --git a/src/zope/pagetemplate/tests/test_ptfile.py b/src/zope/pagetemplate/tests/test_ptfile.py
index d6ecbcd..e5cc2e9 100644
--- a/src/zope/pagetemplate/tests/test_ptfile.py
+++ b/src/zope/pagetemplate/tests/test_ptfile.py
@@ -20,23 +20,19 @@ import unittest
import six
from zope.pagetemplate.pagetemplatefile import PageTemplateFile
+class AbstractPTCase(object):
-class TypeSniffingTestCase(unittest.TestCase):
-
- TEMPFILENAME = tempfile.mktemp()
-
- def tearDown(self):
- if os.path.exists(self.TEMPFILENAME):
- os.unlink(self.TEMPFILENAME)
-
- def get_pt(self, text):
- f = open(self.TEMPFILENAME, "wb")
- f.write(text)
- f.close()
- pt = PageTemplateFile(self.TEMPFILENAME)
+ def get_pt(self, text=b'<html />'):
+ with tempfile.NamedTemporaryFile(mode='wb', delete=False) as f:
+ f.write(text)
+ self.addCleanup(os.unlink, f.name)
+ pt = PageTemplateFile(f.name)
pt.read()
return pt
+class TypeSniffingTestCase(AbstractPTCase,
+ unittest.TestCase):
+
def check_content_type(self, text, expected_type):
pt = self.get_pt(text)
self.assertEqual(pt.content_type, expected_type)
@@ -123,17 +119,18 @@ class TypeSniffingTestCase(unittest.TestCase):
def test_sniffer_html_ascii(self):
self.check_content_type(
("<!DOCTYPE html [ SYSTEM '%s' ]><html></html>"
- % self.HTML_SYSTEM_ID).encode("utf-8"),
+ % self.HTML_SYSTEM_ID).encode("utf-8"),
"text/html")
self.check_content_type(
b"<html><head><title>sample document</title></head></html>",
"text/html")
- # TODO: This reflects a case that simply isn't handled by the
- # sniffer; there are many, but it gets it right more often than
- # before.
- def donttest_sniffer_xml_simple(self):
- self.check_content_type("<doc><element/></doc>",
+ @unittest.expectedFailure
+ def test_sniffer_xml_simple(self):
+ # TODO: This reflects a case that simply isn't handled by the
+ # sniffer; there are many, but it gets it right more often than
+ # before. This case actually returns text/html
+ self.check_content_type(b"<doc><element/></doc>",
"text/xml")
def test_html_default_encoding(self):
@@ -145,9 +142,9 @@ class TypeSniffingTestCase(unittest.TestCase):
rendered = pt()
self.assertTrue(isinstance(rendered, six.text_type))
self.assertEqual(rendered.strip(),
- six.u("<html><head><title>"
- "\u0422\u0435\u0441\u0442"
- "</title></head></html>"))
+ (u"<html><head><title>"
+ u"\u0422\u0435\u0441\u0442"
+ u"</title></head></html>"))
def test_html_encoding_by_meta(self):
pt = self.get_pt(
@@ -160,9 +157,9 @@ class TypeSniffingTestCase(unittest.TestCase):
rendered = pt()
self.assertTrue(isinstance(rendered, six.text_type))
self.assertEqual(rendered.strip(),
- six.u("<html><head><title>"
- "\u0422\u0435\u0441\u0442"
- "</title></head></html>"))
+ (u"<html><head><title>"
+ u"\u0422\u0435\u0441\u0442"
+ u"</title></head></html>"))
def test_xhtml(self):
pt = self.get_pt(
@@ -175,14 +172,51 @@ class TypeSniffingTestCase(unittest.TestCase):
rendered = pt()
self.assertTrue(isinstance(rendered, six.text_type))
self.assertEqual(rendered.strip(),
- six.u("<html><head><title>"
- "\u0422\u0435\u0441\u0442"
- "</title></head></html>"))
+ (u"<html><head><title>"
+ u"\u0422\u0435\u0441\u0442"
+ u"</title></head></html>"))
+
+
+class TestPageTemplateFile(AbstractPTCase,
+ unittest.TestCase):
+
+ def test_no_such_file(self):
+ with self.assertRaises(ValueError):
+ PageTemplateFile('this file does not exist')
+
+ def test_prefix_str(self):
+ pt = PageTemplateFile(os.path.basename(__file__),
+ _prefix=os.path.dirname(__file__))
+ self.assertEqual(pt.filename, __file__)
+
+
+ def test_cook_no_debug(self):
+ pt = self.get_pt()
+ pt._v_debug = False
+ pt._cook_check()
+ self.assertTrue(pt._v_last_read)
+ lr = pt._v_last_read
+ pt._cook_check()
+ self.assertEqual(lr, pt._v_last_read)
+
+
+ def test_cook_mtime_fails(self):
+ pt = self.get_pt()
+ getmtime = os.path.getmtime
+ def bad(_path):
+ raise OSError()
+ os.path.getmtime = bad
+ try:
+ pt._cook_check()
+ finally:
+ os.path.getmtime = getmtime
+ self.assertEqual(0, pt._v_last_read)
-def test_suite():
- return unittest.makeSuite(TypeSniffingTestCase)
+ def test_pickle_not_allowed(self):
+ import pickle
+ pt = self.get_pt()
-if __name__ == "__main__":
- unittest.main(defaultTest="test_suite")
+ with self.assertRaises(TypeError):
+ pickle.dumps(pt)
diff --git a/src/zope/pagetemplate/tests/util.py b/src/zope/pagetemplate/tests/util.py
index 8dda99b..5ef2ad3 100644
--- a/src/zope/pagetemplate/tests/util.py
+++ b/src/zope/pagetemplate/tests/util.py
@@ -17,71 +17,46 @@ from __future__ import print_function
import os
import re
import sys
-
-
-class Bruce(object):
- __allow_access_to_unprotected_subobjects__=1
- def __str__(self): return 'bruce'
- def __int__(self): return 42
- def __float__(self): return 42.0
- def keys(self): return ['bruce']*7
- def values(self): return [self]*7
- def items(self): return [('bruce',self)]*7
- def __len__(self): return 7
- def __getitem__(self,index):
- if isinstance(index, int) and (index < 0 or index > 6):
- raise IndexError(index)
- return self
- isDocTemp = 0
- def __getattr__(self,name):
- if name.startswith('_'):
- raise AttributeError(name)
- return self
-
-bruce = Bruce()
+import unittest
+import zope.pagetemplate.tests
class arg(object):
__allow_access_to_unprotected_subobjects__ = 1
- def __init__(self,nn,aa): self.num, self.arg = nn, aa
- def __str__(self): return str(self.arg)
+
+ def __init__(self, nn, aa):
+ self.num, self.arg = nn, aa
+
+ def __str__(self):
+ return str(self.arg)
class argv(object):
__allow_access_to_unprotected_subobjects__ = 1
- def __init__(self, argv=sys.argv[1:]):
+ def __init__(self, argv=None):
args = self.args = []
+ argv = argv if argv is not None else sys.argv[1:]
for aa in argv:
- args.append(arg(len(args)+1,aa))
-
- def items(self):
- return map(lambda a: ('spam%d' % a.num, a), self.args)
-
- def values(self): return self.args
-
- def getRoot(self):
- return self
+ args.append(arg(len(args) + 1, aa))
context = property(lambda self: self)
-def nicerange(lo, hi):
- if hi <= lo+1:
- return str(lo+1)
- else:
- return "%d,%d" % (lo+1, hi)
+class _Test(unittest.TestCase):
+
+ def runTest(self): # pragma: no cover 2.7 compatibility
+ return
-def dump(tag, x, lo, hi):
- for i in xrange(lo, hi):
- print('%s %s' % (tag, x[i]), end=' ')
+_assertEqual = _Test().assertEqual
+del _Test
def check_html(s1, s2):
s1 = normalize_html(s1)
s2 = normalize_html(s2)
- assert s1==s2, (s1, s2, "HTML Output Changed")
+ _assertEqual(s1, s2, "HTML Output Changed")
def check_xml(s1, s2):
s1 = normalize_xml(s1)
s2 = normalize_xml(s2)
- assert s1==s2, ("XML Output Changed:\n%r\n\n%r" % (s1, s2))
+ _assertEqual(s1, s2, 'XML Output Changed')
def normalize_html(s):
s = re.sub(r"[ \t]+", " ", s)
@@ -95,11 +70,11 @@ def normalize_xml(s):
return s
-import zope.pagetemplate.tests
-dir = os.path.dirname(zope.pagetemplate.tests.__file__)
-input_dir = os.path.join(dir, 'input')
-output_dir = os.path.join(dir, 'output')
+
+here = os.path.dirname(zope.pagetemplate.tests.__file__)
+input_dir = os.path.join(here, 'input')
+output_dir = os.path.join(here, 'output')
def read_input(filename):
filename = os.path.join(input_dir, filename)
diff --git a/tox.ini b/tox.ini
index 0476758..90869b2 100644
--- a/tox.ini
+++ b/tox.ini
@@ -14,7 +14,7 @@ basepython =
python2.7
commands =
coverage run -m zope.testrunner --test-path=src []
- coverage report --fail-under=88
+ coverage report --fail-under=100
deps =
{[testenv]deps}
coverage