Skip to content

Commit

Permalink
Restore testing.PlacelessSetup and friends.
Browse files Browse the repository at this point in the history
Also move the two new views that were added to tests and out of
testing because those are not public APIs.
  • Loading branch information
jamadden committed Apr 26, 2017
1 parent c0c781a commit 8801ecb
Show file tree
Hide file tree
Showing 7 changed files with 251 additions and 37 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Expand Up @@ -18,7 +18,7 @@ install:
- pip install -U pip setuptools
- pip install -U coveralls coverage
# XXX Temporary
- pip install git+https://github.com/zopefoundation/zope.app.rotterdam@python3#egg=zope.app.rotterdam
- pip install git+https://github.com/zopefoundation/zope.app.rotterdam@master#egg=zope.app.rotterdam
- pip install -U -e ".[test]"


Expand Down
6 changes: 4 additions & 2 deletions CHANGES.rst
Expand Up @@ -11,8 +11,10 @@ CHANGES
- Remove install dependency on zope.app.form, replaced with direct
imports of zope.formlib.

- Remove most contents of zope.app.component.testing since they relied
on the deprecated zope.app.testing.
- Simplify ``zope.app.component.testing`` to remove the deprecated or
broken functionality in ``testingNextUtility`` and ``SiteManagerStub``.
``PlacefulSetup`` is retained, although use of
``zope.component.testing.PlacelessSetup`` is suggested when possible.

- Add support for PyPy and Python 3.4, 3.5 and 3.6.

Expand Down
6 changes: 3 additions & 3 deletions src/zope/app/component/ftesting.zcml
Expand Up @@ -82,7 +82,7 @@
<browser:page
name="login_logout"
for="*"
class=".testing.LoginLogout"
class=".tests.LoginLogout"
permission="zope.Public"
/>

Expand Down Expand Up @@ -124,7 +124,7 @@
for="*"
name="SelectedManagementView.html"
permission="zope.Public"
class=".testing.ManagementViewSelector"
class=".tests.ManagementViewSelector"
allowed_interface="zope.publisher.interfaces.browser.IBrowserPublisher"
/>

Expand All @@ -133,7 +133,7 @@
for="*"
name="manage"
permission="zope.ManageContent"
class=".testing.ManagementViewSelector"
class=".tests.ManagementViewSelector"
allowed_interface="zope.publisher.interfaces.browser.IBrowserPublisher"
/>

Expand Down
175 changes: 145 additions & 30 deletions src/zope/app/component/testing.py
Expand Up @@ -13,42 +13,157 @@
##############################################################################
"""
Test helpers.
This is part of the public API of this package.
"""
from __future__ import absolute_import

import zope.interface
import zope.site.folder
from zope import component

from zope.component.hooks import setSite
from zope.component.interfaces import ISite
from zope.component.testing import PlacelessSetup

from zope.container.interfaces import ISimpleReadContainer
from zope.container.traversal import ContainerTraversable

from zope.publisher.browser import BrowserView
from zope.publisher.interfaces.browser import IBrowserPublisher
from zope.browsermenu.menu import getFirstMenuItem
from zope.site.folder import Folder
from zope.site.folder import rootFolder
from zope.site.site import LocalSiteManager

from zope.traversing.api import traverse
from zope.traversing.interfaces import ITraversable

@zope.interface.implementer(IBrowserPublisher)
class ManagementViewSelector(BrowserView):
"""View that selects the first available management view.
def buildSampleFolderTree():
"""
Create a tree of folders and return the root::
Support 'zmi_views' actions like: 'javascript:alert("hello")',
'../view_on_parent.html' or '++rollover++'.
____________ rootFolder ______________________________
/ \ \
folder1 __________________ folder2 folder3
| \ | |
folder1_1 ____ folder1_2 folder2_1 folder3_1
| \ | |
folder1_1_1 folder1_1_2 folder1_2_1 folder2_1_1
"""
# Copied from zope.app.publication
# Simplified to assert just the test case we expect.

def browserDefault(self, request):
return self, ()

def __call__(self):
item = getFirstMenuItem('zmi_views', self.context, self.request)
assert item
redirect_url = item['action']
if not redirect_url.lower().startswith(('../', 'javascript:', '++')):
self.request.response.redirect(redirect_url)
return u''
raise AssertionError("Should not get here") # pragma: no cover

class LoginLogout(object):
# Dummy implementation of zope.app.security.browser.auth.LoginLogout

def __call__(self):
return None

root = rootFolder()
root[u'folder1'] = Folder()
root[u'folder1'][u'folder1_1'] = Folder()
root[u'folder1'][u'folder1_1'][u'folder1_1_1'] = Folder()
root[u'folder1'][u'folder1_1'][u'folder1_1_2'] = Folder()
root[u'folder1'][u'folder1_2'] = Folder()
root[u'folder1'][u'folder1_2'][u'folder1_2_1'] = Folder()
root[u'folder2'] = Folder()
root[u'folder2'][u'folder2_1'] = Folder()
root[u'folder2'][u'folder2_1'][u'folder2_1_1'] = Folder()
root[u"\N{CYRILLIC SMALL LETTER PE}"
u"\N{CYRILLIC SMALL LETTER A}"
u"\N{CYRILLIC SMALL LETTER PE}"
u"\N{CYRILLIC SMALL LETTER KA}"
u"\N{CYRILLIC SMALL LETTER A}3"] = Folder()
root[u"\N{CYRILLIC SMALL LETTER PE}"
u"\N{CYRILLIC SMALL LETTER A}"
u"\N{CYRILLIC SMALL LETTER PE}"
u"\N{CYRILLIC SMALL LETTER KA}"
u"\N{CYRILLIC SMALL LETTER A}3"][
u"\N{CYRILLIC SMALL LETTER PE}"
u"\N{CYRILLIC SMALL LETTER A}"
u"\N{CYRILLIC SMALL LETTER PE}"
u"\N{CYRILLIC SMALL LETTER KA}"
u"\N{CYRILLIC SMALL LETTER A}3_1"] = Folder()

return root


def createSiteManager(folder, setsite=False):
"Make the given folder a site, and optionally make it the current site."
if not ISite.providedBy(folder):
folder.setSiteManager(LocalSiteManager(folder))
if setsite:
setSite(folder)
return traverse(folder, "++etc++site")


def setUpTraversal():
"Make simple read containers traversable."
from zope.traversing.testing import setUp
setUp()
component.provideAdapter(ContainerTraversable,
(ISimpleReadContainer,),
ITraversable)


class Place(object):
"A property-like descriptor that traverses its name starting from 'rootFolder."
def __init__(self, path):
self.path = path

def __get__(self, inst, cls=None):
if inst is None: # pragma: no cover
return self

try:
# Use __dict__ directly to avoid infinite recursion
root = inst.__dict__['rootFolder']
except KeyError:
root = inst.rootFolder = buildSampleFolderTree()

return traverse(root, self.path)


class PlacefulSetup(PlacelessSetup):
"""
A unittest fixture that optionally creates many folders and a site.
In many cases, :class:`zope.component.testing.PlacelessSetup` is
sufficient.
"""

# Places :)
rootFolder = Place(u'')

folder1 = Place(u'folder1')
folder1_1 = Place(u'folder1/folder1_1')
folder1_1_1 = Place(u'folder1/folder1_1/folder1_1_1')
folder1_1_2 = Place(u'folder1/folder1_2/folder1_1_2')
folder1_2 = Place(u'folder1/folder1_2')
folder1_2_1 = Place(u'folder1/folder1_2/folder1_2_1')

folder2 = Place(u'folder2')
folder2_1 = Place(u'folder2/folder2_1')
folder2_1_1 = Place(u'folder2/folder2_1/folder2_1_1')

folder3 = Place(u"\N{CYRILLIC SMALL LETTER PE}"
u"\N{CYRILLIC SMALL LETTER A}"
u"\N{CYRILLIC SMALL LETTER PE}"
u"\N{CYRILLIC SMALL LETTER KA}"
u"\N{CYRILLIC SMALL LETTER A}3")
folder3_1 = Place(u"\N{CYRILLIC SMALL LETTER PE}"
u"\N{CYRILLIC SMALL LETTER A}"
u"\N{CYRILLIC SMALL LETTER PE}"
u"\N{CYRILLIC SMALL LETTER KA}"
u"\N{CYRILLIC SMALL LETTER A}3/"
u"\N{CYRILLIC SMALL LETTER PE}"
u"\N{CYRILLIC SMALL LETTER A}"
u"\N{CYRILLIC SMALL LETTER PE}"
u"\N{CYRILLIC SMALL LETTER KA}"
u"\N{CYRILLIC SMALL LETTER A}3_1")

def setUp(self, folders=False, site=False):
PlacelessSetup.setUp(self)
setUpTraversal()
if folders or site:
return self.buildFolders(site)

def buildFolders(self, site=False):
self.rootFolder = buildSampleFolderTree()
if site:
return self.makeSite()

def makeSite(self, path='/'):
folder = traverse(self.rootFolder, path)
return createSiteManager(folder, True)

def createRootFolder(self):
self.rootFolder = rootFolder()
49 changes: 49 additions & 0 deletions src/zope/app/component/tests/__init__.py
@@ -0,0 +1,49 @@
##############################################################################
#
# Copyright (c) 2001-2007 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"Non-API test details."


from zope.interface import implementer
from zope.publisher.browser import BrowserView
from zope.publisher.interfaces.browser import IBrowserPublisher
from zope.browsermenu.menu import getFirstMenuItem


@implementer(IBrowserPublisher)
class ManagementViewSelector(BrowserView):
"""View that selects the first available management view.
Support 'zmi_views' actions like: 'javascript:alert("hello")',
'../view_on_parent.html' or '++rollover++'.
"""
# Copied from zope.app.publication
# Simplified to assert just the test case we expect.

def browserDefault(self, request):
return self, ()

def __call__(self):
item = getFirstMenuItem('zmi_views', self.context, self.request)
assert item
redirect_url = item['action']
if not redirect_url.lower().startswith(('../', 'javascript:', '++')):
self.request.response.redirect(redirect_url)
return u''
raise AssertionError("Should not get here") # pragma: no cover

class LoginLogout(object):
# Dummy implementation of zope.app.security.browser.auth.LoginLogout

def __call__(self):
return None
48 changes: 48 additions & 0 deletions src/zope/app/component/tests/test_testing.py
@@ -0,0 +1,48 @@
#############################################################################
#
# Copyright (c) 2017 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################

import unittest
from zope.app.component import testing

from zope.component.interfaces import ISite

class TestTesting(testing.PlacefulSetup,
unittest.TestCase):

def setUp(self):
super(TestTesting, self).setUp(site=True)

def test_is_site(self):
self.assertTrue(ISite.providedBy(self.rootFolder))

def test_build_sample_folder(self):
# We don't actually test the details of the layout, just some basics
tree1 = testing.buildSampleFolderTree()
self.assertIsNotNone(tree1)
self.assertIsNot(tree1, testing.buildSampleFolderTree())
self.assertIn(u'folder1', tree1)

def test_new_root(self):
cur_root = self.rootFolder
self.createRootFolder()
self.assertIsNot(self.rootFolder, cur_root)

def test_place_rebuilds_root(self):
f1 = self.folder1
del self.rootFolder
f2 = self.folder1
self.assertIsNot(f1, f2)

def test_suite():
return unittest.defaultTestLoader.loadTestsFromName(__name__)
2 changes: 1 addition & 1 deletion tox.ini
Expand Up @@ -6,5 +6,5 @@ envlist =
commands =
zope-testrunner --test-path=src []
deps =
git+https://github.com/zopefoundation/zope.app.rotterdam@python3#egg=zope.app.rotterdam
git+https://github.com/zopefoundation/zope.app.rotterdam@master#egg=zope.app.rotterdam
.[test]

0 comments on commit 8801ecb

Please sign in to comment.