123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366 |
- # Copyright (c) Twisted Matrix Laboratories.
- # See LICENSE for details.
- """
- Tests for L{twisted.web.util}.
- """
- from __future__ import absolute_import, division
- import gc
- from twisted.python.failure import Failure
- from twisted.trial.unittest import SynchronousTestCase, TestCase
- from twisted.internet import defer
- from twisted.python.compat import _PY3, intToBytes, networkString
- from twisted.web import resource, util
- from twisted.web.error import FlattenerError
- from twisted.web.http import FOUND
- from twisted.web.server import Request
- from twisted.web.template import TagLoader, flattenString, tags
- from twisted.web.test.requesthelper import DummyChannel, DummyRequest
- from twisted.web.util import DeferredResource
- from twisted.web.util import _SourceFragmentElement, _FrameElement
- from twisted.web.util import _StackElement, FailureElement, formatFailure
- from twisted.web.util import redirectTo, _SourceLineElement
- class RedirectToTests(TestCase):
- """
- Tests for L{redirectTo}.
- """
- def test_headersAndCode(self):
- """
- L{redirectTo} will set the C{Location} and C{Content-Type} headers on
- its request, and set the response code to C{FOUND}, so the browser will
- be redirected.
- """
- request = Request(DummyChannel(), True)
- request.method = b'GET'
- targetURL = b"http://target.example.com/4321"
- redirectTo(targetURL, request)
- self.assertEqual(request.code, FOUND)
- self.assertEqual(
- request.responseHeaders.getRawHeaders(b'location'), [targetURL])
- self.assertEqual(
- request.responseHeaders.getRawHeaders(b'content-type'),
- [b'text/html; charset=utf-8'])
- def test_redirectToUnicodeURL(self) :
- """
- L{redirectTo} will raise TypeError if unicode object is passed in URL
- """
- request = Request(DummyChannel(), True)
- request.method = b'GET'
- targetURL = u'http://target.example.com/4321'
- self.assertRaises(TypeError, redirectTo, targetURL, request)
- class FailureElementTests(TestCase):
- """
- Tests for L{FailureElement} and related helpers which can render a
- L{Failure} as an HTML string.
- """
- def setUp(self):
- """
- Create a L{Failure} which can be used by the rendering tests.
- """
- def lineNumberProbeAlsoBroken():
- message = "This is a problem"
- raise Exception(message)
- # Figure out the line number from which the exception will be raised.
- self.base = lineNumberProbeAlsoBroken.__code__.co_firstlineno + 1
- try:
- lineNumberProbeAlsoBroken()
- except:
- self.failure = Failure(captureVars=True)
- self.frame = self.failure.frames[-1]
- def test_sourceLineElement(self):
- """
- L{_SourceLineElement} renders a source line and line number.
- """
- element = _SourceLineElement(
- TagLoader(tags.div(
- tags.span(render="lineNumber"),
- tags.span(render="sourceLine"))),
- 50, " print 'hello'")
- d = flattenString(None, element)
- expected = (
- u"<div><span>50</span><span>"
- u" \N{NO-BREAK SPACE} \N{NO-BREAK SPACE}print 'hello'</span></div>")
- d.addCallback(
- self.assertEqual, expected.encode('utf-8'))
- return d
- def test_sourceFragmentElement(self):
- """
- L{_SourceFragmentElement} renders source lines at and around the line
- number indicated by a frame object.
- """
- element = _SourceFragmentElement(
- TagLoader(tags.div(
- tags.span(render="lineNumber"),
- tags.span(render="sourceLine"),
- render="sourceLines")),
- self.frame)
- source = [
- u' \N{NO-BREAK SPACE} \N{NO-BREAK SPACE}message = '
- u'"This is a problem"',
- u' \N{NO-BREAK SPACE} \N{NO-BREAK SPACE}raise Exception(message)',
- u'# Figure out the line number from which the exception will be '
- u'raised.',
- ]
- d = flattenString(None, element)
- if _PY3:
- stringToCheckFor = ''.join([
- '<div class="snippet%sLine"><span>%d</span><span>%s</span>'
- '</div>' % (
- ["", "Highlight"][lineNumber == 1],
- self.base + lineNumber,
- (u" \N{NO-BREAK SPACE}" * 4 + sourceLine))
- for (lineNumber, sourceLine)
- in enumerate(source)]).encode("utf8")
- else:
- stringToCheckFor = ''.join([
- '<div class="snippet%sLine"><span>%d</span><span>%s</span>'
- '</div>' % (
- ["", "Highlight"][lineNumber == 1],
- self.base + lineNumber,
- (u" \N{NO-BREAK SPACE}" * 4 + sourceLine).encode('utf8'))
- for (lineNumber, sourceLine)
- in enumerate(source)])
- d.addCallback(self.assertEqual, stringToCheckFor)
- return d
- def test_frameElementFilename(self):
- """
- The I{filename} renderer of L{_FrameElement} renders the filename
- associated with the frame object used to initialize the
- L{_FrameElement}.
- """
- element = _FrameElement(
- TagLoader(tags.span(render="filename")),
- self.frame)
- d = flattenString(None, element)
- d.addCallback(
- # __file__ differs depending on whether an up-to-date .pyc file
- # already existed.
- self.assertEqual,
- b"<span>" + networkString(__file__.rstrip('c')) + b"</span>")
- return d
- def test_frameElementLineNumber(self):
- """
- The I{lineNumber} renderer of L{_FrameElement} renders the line number
- associated with the frame object used to initialize the
- L{_FrameElement}.
- """
- element = _FrameElement(
- TagLoader(tags.span(render="lineNumber")),
- self.frame)
- d = flattenString(None, element)
- d.addCallback(
- self.assertEqual, b"<span>" + intToBytes(self.base + 1) + b"</span>")
- return d
- def test_frameElementFunction(self):
- """
- The I{function} renderer of L{_FrameElement} renders the line number
- associated with the frame object used to initialize the
- L{_FrameElement}.
- """
- element = _FrameElement(
- TagLoader(tags.span(render="function")),
- self.frame)
- d = flattenString(None, element)
- d.addCallback(
- self.assertEqual, b"<span>lineNumberProbeAlsoBroken</span>")
- return d
- def test_frameElementSource(self):
- """
- The I{source} renderer of L{_FrameElement} renders the source code near
- the source filename/line number associated with the frame object used to
- initialize the L{_FrameElement}.
- """
- element = _FrameElement(None, self.frame)
- renderer = element.lookupRenderMethod("source")
- tag = tags.div()
- result = renderer(None, tag)
- self.assertIsInstance(result, _SourceFragmentElement)
- self.assertIdentical(result.frame, self.frame)
- self.assertEqual([tag], result.loader.load())
- def test_stackElement(self):
- """
- The I{frames} renderer of L{_StackElement} renders each stack frame in
- the list of frames used to initialize the L{_StackElement}.
- """
- element = _StackElement(None, self.failure.frames[:2])
- renderer = element.lookupRenderMethod("frames")
- tag = tags.div()
- result = renderer(None, tag)
- self.assertIsInstance(result, list)
- self.assertIsInstance(result[0], _FrameElement)
- self.assertIdentical(result[0].frame, self.failure.frames[0])
- self.assertIsInstance(result[1], _FrameElement)
- self.assertIdentical(result[1].frame, self.failure.frames[1])
- # They must not share the same tag object.
- self.assertNotEqual(result[0].loader.load(), result[1].loader.load())
- self.assertEqual(2, len(result))
- def test_failureElementTraceback(self):
- """
- The I{traceback} renderer of L{FailureElement} renders the failure's
- stack frames using L{_StackElement}.
- """
- element = FailureElement(self.failure)
- renderer = element.lookupRenderMethod("traceback")
- tag = tags.div()
- result = renderer(None, tag)
- self.assertIsInstance(result, _StackElement)
- self.assertIdentical(result.stackFrames, self.failure.frames)
- self.assertEqual([tag], result.loader.load())
- def test_failureElementType(self):
- """
- The I{type} renderer of L{FailureElement} renders the failure's
- exception type.
- """
- element = FailureElement(
- self.failure, TagLoader(tags.span(render="type")))
- d = flattenString(None, element)
- if _PY3:
- exc = b"builtins.Exception"
- else:
- exc = b"exceptions.Exception"
- d.addCallback(
- self.assertEqual, b"<span>" + exc + b"</span>")
- return d
- def test_failureElementValue(self):
- """
- The I{value} renderer of L{FailureElement} renders the value's exception
- value.
- """
- element = FailureElement(
- self.failure, TagLoader(tags.span(render="value")))
- d = flattenString(None, element)
- d.addCallback(
- self.assertEqual, b'<span>This is a problem</span>')
- return d
- class FormatFailureTests(TestCase):
- """
- Tests for L{twisted.web.util.formatFailure} which returns an HTML string
- representing the L{Failure} instance passed to it.
- """
- def test_flattenerError(self):
- """
- If there is an error flattening the L{Failure} instance,
- L{formatFailure} raises L{FlattenerError}.
- """
- self.assertRaises(FlattenerError, formatFailure, object())
- def test_returnsBytes(self):
- """
- The return value of L{formatFailure} is a C{str} instance (not a
- C{unicode} instance) with numeric character references for any non-ASCII
- characters meant to appear in the output.
- """
- try:
- raise Exception("Fake bug")
- except:
- result = formatFailure(Failure())
- self.assertIsInstance(result, bytes)
- if _PY3:
- self.assertTrue(all(ch < 128 for ch in result))
- else:
- self.assertTrue(all(ord(ch) < 128 for ch in result))
- # Indentation happens to rely on NO-BREAK SPACE
- self.assertIn(b" ", result)
- class SDResource(resource.Resource):
- def __init__(self,default):
- self.default = default
- def getChildWithDefault(self, name, request):
- d = defer.succeed(self.default)
- resource = util.DeferredResource(d)
- return resource.getChildWithDefault(name, request)
- class DeferredResourceTests(SynchronousTestCase):
- """
- Tests for L{DeferredResource}.
- """
- def testDeferredResource(self):
- r = resource.Resource()
- r.isLeaf = 1
- s = SDResource(r)
- d = DummyRequest(['foo', 'bar', 'baz'])
- resource.getChildForRequest(s, d)
- self.assertEqual(d.postpath, ['bar', 'baz'])
- def test_render(self):
- """
- L{DeferredResource} uses the request object's C{render} method to
- render the resource which is the result of the L{Deferred} being
- handled.
- """
- rendered = []
- request = DummyRequest([])
- request.render = rendered.append
- result = resource.Resource()
- deferredResource = DeferredResource(defer.succeed(result))
- deferredResource.render(request)
- self.assertEqual(rendered, [result])
- def test_renderNoFailure(self):
- """
- If the L{Deferred} fails, L{DeferredResource} reports the failure via
- C{processingFailed}, and does not cause an unhandled error to be
- logged.
- """
- request = DummyRequest([])
- d = request.notifyFinish()
- failure = Failure(RuntimeError())
- deferredResource = DeferredResource(defer.fail(failure))
- deferredResource.render(request)
- self.assertEqual(self.failureResultOf(d), failure)
- del deferredResource
- gc.collect()
- errors = self.flushLoggedErrors(RuntimeError)
- self.assertEqual(errors, [])
|