changeset 4038:5467ef800d48

merged main
author Byeongweon [tasyblue@gmail.com]
date Tue, 26 Aug 2008 10:25:31 +0900
parents e5a5b2797a6a (current diff) 48e8f892ee87 (diff)
children 532865ba8334
files MoinMoin/_tests/compat.py MoinMoin/config/_tests/test_configs.py tests/wikiconfig.py
diffstat 23 files changed, 294 insertions(+), 379 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/_tests/_test_template.py	Thu Aug 21 16:34:39 2008 +0900
+++ b/MoinMoin/_tests/_test_template.py	Tue Aug 26 10:25:31 2008 +0900
@@ -45,19 +45,17 @@
         ('Line break',  '<<BR>>',        '<br>'),
     )
 
+    from MoinMoin._tests import wikiconfig
+    class Config(wikiconfig.Config):
+        foo = 'bar'  # we want to have this non-default setting
+
     def setup_class(self):
         """ Stuff that should be run to init the state of this test class
-
-        Some test needs specific config values, or they will fail.
         """
-        self.config = self.TestConfig(defaults=['this option', 'that option'],
-                                      another_option='non default value')
 
     def teardown_class(self):
         """ Stuff that should run to clean up the state of this test class
-
         """
-        self.config.reset()
 
     def testFunction(self):
         """ module_tested: function should... """
--- a/MoinMoin/_tests/compat.py	Thu Aug 21 16:34:39 2008 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,0 @@
-""" UnitTest compatiblity code, from the py lib. MIT licensed originally, copyright Holger Krekel. """
-
-import py
-from py.__.test.outcome import Failed, Passed
-
-
-class TestCaseUnit(py.test.collect.Function):
-    """ compatibility Unit executor for TestCase methods
-        honouring setUp and tearDown semantics.
-    """
-    def execute(self, session):
-        boundmethod = self.obj
-        instance = boundmethod.im_self
-        instance.setUp()
-        try:
-            boundmethod()
-        finally:
-            instance.tearDown()
-        return Passed()
-
-class TestCase(object):
-    """compatibility class of unittest's TestCase. """
-    Function = TestCaseUnit
-
-    def setUp(self):
-        pass
-
-    def tearDown(self):
-        pass
-
-    def fail(self, msg=None):
-        """ fail immediate with given message. """
-        raise Failed(msg=msg)
-
-    def assertRaises(self, excclass, func, *args, **kwargs):
-        py.test.raises(excclass, func, *args, **kwargs)
-    failUnlessRaises = assertRaises
-
-    # dynamically construct (redundant) methods
-    aliasmap = [
-        ('x',   'not x', 'assert_, failUnless'),
-        ('x',   'x',     'failIf'),
-        ('x,y', 'x!=y',  'failUnlessEqual,assertEqual, assertEquals'),
-        ('x,y', 'x==y',  'failIfEqual,assertNotEqual, assertNotEquals'),
-        ]
-    items = []
-    for sig, expr, names in aliasmap:
-        names = map(str.strip, names.split(','))
-        sigsubst = expr.replace('y', '%s').replace('x', '%s')
-        for name in names:
-            items.append("""
-                def %(name)s(self, %(sig)s, msg=""):
-                    __tracebackhide__ = True
-                    if %(expr)s:
-                        raise Failed(msg=msg + (%(sigsubst)r %% (%(sig)s)))
-            """ % locals() )
-
-    source = "".join(items)
-    exec py.code.Source(source).compile()
-
-__all__ = ['TestCase']
--- a/MoinMoin/_tests/test_PageEditor.py	Thu Aug 21 16:34:39 2008 +0900
+++ b/MoinMoin/_tests/test_PageEditor.py	Tue Aug 26 10:25:31 2008 +0900
@@ -73,11 +73,7 @@
 
     def testExtendedNamesEnabled(self):
         """ PageEditor: expand @USERNAME@ extended name - enabled """
-        try:
-            config = self.TestConfig()
-            assert self.expand() == u'[[%s]]' % self.name
-        finally:
-            del config
+        assert self.expand() == u'[[%s]]' % self.name
 
 
 class TestExpandMailto(TestExpandUserName):
--- a/MoinMoin/_tests/test_user.py	Thu Aug 21 16:34:39 2008 +0900
+++ b/MoinMoin/_tests/test_user.py	Tue Aug 26 10:25:31 2008 +0900
@@ -178,19 +178,9 @@
 
 class TestGroupName(object):
 
-    def setUp(self):
-        self.config = self.TestConfig(page_group_regex=r'.+Group')
-
-    def tearDown(self):
-        del self.config
-
-    import re
-    group = re.compile(r'.+Group', re.UNICODE)
-
     def testGroupNames(self):
         """ user: isValidName: reject group names """
         test = u'AdminGroup'
-        assert self.group.search(test)
         assert not user.isValidName(self.request, test)
 
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/_tests/wikiconfig.py	Tue Aug 26 10:25:31 2008 +0900
@@ -0,0 +1,33 @@
+# -*- coding: iso-8859-1 -*-
+"""
+MoinMoin - test wiki configuration
+
+Do not change any values without good reason.
+
+We mostly want to have default values here, except for stuff that doesn't
+work without setting them (like data_dir and underlay_dir).
+
+@copyright: 2000-2004 by Juergen Hermann <jh@web.de>
+@license: GNU GPL, see COPYING for details.
+"""
+
+import os
+
+from MoinMoin.config.multiconfig import DefaultConfig
+
+
+class Config(DefaultConfig):
+    sitename = u'Developer Test Wiki'
+    logo_string = sitename
+
+    _base_dir = os.path.join(os.path.dirname(__file__), '../../tests/wiki')
+    data_dir = os.path.join(_base_dir, "data")
+    data_underlay_dir = os.path.join(_base_dir, "underlay")
+
+    #show_hosts = 1
+
+    #secrets = 'some not secret string just to make tests happy'
+
+    # used to check if it is really a wiki we may modify
+    is_test_wiki = True
+
--- a/MoinMoin/action/fckdialog.py	Thu Aug 21 16:34:39 2008 +0900
+++ b/MoinMoin/action/fckdialog.py	Tue Aug 26 10:25:31 2008 +0900
@@ -7,7 +7,7 @@
 """
 
 from MoinMoin import config, wikiutil
-import re
+import re
 
 ##############################################################################
 ### Macro dialog
@@ -238,15 +238,15 @@
     else:
         resultlist = iwpreferred[:-1]
     interwiki = "\n".join(
-        ['<option value="%s">%s</option>' % (key, key) for key in resultlist])
-    
+        ['<option value="%s">%s</option>' % (key, key) for key in resultlist])
+
     # wiki url
     url_prefix_static = request.cfg.url_prefix_static
     scriptname = request.getScriptname()
     if not scriptname or scriptname[-1] != "/":
         scriptname += "/"
     action = scriptname
-    basepage = request.page.page_name.encode(config.charset)
+    basepage = request.page.page_name.encode(config.charset)
     request.write('''
 <!--
  * FCKeditor - The text editor for internet
@@ -323,7 +323,7 @@
          <td>
           <span fckLang="WikiDlgName">Wiki:PageName</span><br>
           <select id="sctInterwiki" size="1">
-          %(interwiki)s 
+          %(interwiki)s
           </select>:
           <input id="txtInterwikipagename"></input>
          </td>
--- a/MoinMoin/auth/_tests/test_auth.py	Thu Aug 21 16:34:39 2008 +0900
+++ b/MoinMoin/auth/_tests/test_auth.py	Tue Aug 26 10:25:31 2008 +0900
@@ -6,13 +6,17 @@
     @license: GNU GPL, see COPYING for details.
 """
 
+import py.test
+py.test.skip("broken due to test Config refactoring, fix later")
+
 import StringIO, urllib
 
 from MoinMoin.server.server_wsgi import WsgiConfig
 from MoinMoin.request import request_wsgi
+from MoinMoin._tests import wikiconfig
 
 
-class TestAuth:
+class AuthTest:
     """ test misc. auth methods """
     PAGES = ['FrontPage', 'MoinMoin', 'HelpContents', 'WikiSandBox', ] # must all exist!
 
@@ -54,6 +58,8 @@
         request.run()
         return request # request.status, request.headers, request.output()
 
+
+class TestNoAuth(AuthTest):
     def testNoAuth(self):
         """ run a simple request, no auth, just check if it succeeds """
         environ = self.setup_env()
@@ -85,9 +91,12 @@
         output = request.output()
         assert '</html>' in output
 
+class TestAnonSession(AuthTest):
+    class Config(wikiconfig.Config):
+        anonymous_session_lifetime = 1
+
     def testAnonSession(self):
         """ run some requests, no auth, check if anon sessions work """
-        self.config = self.TestConfig(anonymous_session_lifetime=1)
         cookie = ''
         trail_expected = []
         first = True
@@ -144,11 +153,15 @@
             trail = request.session['trail']
             assert trail == trail_expected
 
+class TestHttpAuthSession(AuthTest):
+    class Config(wikiconfig.Config):
+        from MoinMoin.auth.http import HTTPAuth
+        auth = [HTTPAuth()]
+        user_autocreate = True
+
     def testHttpAuthSession(self):
         """ run some requests with http auth, check whether session works """
-        from MoinMoin.auth.http import HTTPAuth
         username = u'HttpAuthTestUser'
-        self.config = self.TestConfig(auth=[HTTPAuth()], user_autocreate=True)
         cookie = ''
         trail_expected = []
         first = True
@@ -204,11 +217,14 @@
             trail = request.session['trail']
             assert trail == trail_expected
 
+class TestMoinAuthSession(AuthTest):
+    class Config(wikiconfig.Config):
+        from MoinMoin.auth import MoinAuth
+        auth = [MoinAuth()]
+
     def testMoinAuthSession(self):
         """ run some requests with MoinAuth, check whether session works """
-        from MoinMoin.auth import MoinAuth
         from MoinMoin.user import User
-        self.config = self.TestConfig(auth=[MoinAuth()])
         username = u'MoinAuthTestUser'
         password = u'ßecretß'
         User(self.request, name=username, password=password).save() # create user
--- a/MoinMoin/auth/_tests/test_ldap_login.py	Thu Aug 21 16:34:39 2008 +0900
+++ b/MoinMoin/auth/_tests/test_ldap_login.py	Tue Aug 26 10:25:31 2008 +0900
@@ -7,10 +7,11 @@
 """
 
 import py.test
+py.test.skip("Broken due to test Config refactoring")
 
 from MoinMoin._tests.ldap_testbase import LDAPTstBase, LdapEnvironment, check_environ, SLAPD_EXECUTABLE
 from MoinMoin._tests.ldap_testdata import *
-from MoinMoin._tests import nuke_user
+from MoinMoin._tests import nuke_user, wikiconfig
 
 # first check if we have python 2.4, python-ldap and slapd:
 msg = check_environ()
@@ -20,7 +21,7 @@
 
 import ldap
 
-class TestSimpleLdap(LDAPTstBase):
+class TestLDAPServer(LDAPTstBase):
     basedn = BASEDN
     rootdn = ROOTDN
     rootpw = ROOTPW
@@ -39,14 +40,24 @@
         assert 'usera' in uids
         assert 'userb' in uids
 
+class TestMoinLDAPLogin(LDAPTstBase):
+    basedn = BASEDN
+    rootdn = ROOTDN
+    rootpw = ROOTPW
+    slapd_config = SLAPD_CONFIG
+    ldif_content = LDIF_CONTENT
+
+    class Config(wikiconfig.Config):
+        from MoinMoin.auth.ldap_login import LDAPAuth
+        server_uri = self.ldap_env.slapd.url # XXX no self
+        base_dn = self.ldap_env.basedn
+        ldap_auth1 = LDAPAuth(server_uri=server_uri, base_dn=base_dn)
+        auth = [ldap_auth1, ]
+        user_autocreate = True
+
     def testMoinLDAPLogin(self):
         """ Just try accessing the LDAP server and see if usera and userb are in LDAP. """
-        server_uri = self.ldap_env.slapd.url
-        base_dn = self.ldap_env.basedn
 
-        from MoinMoin.auth.ldap_login import LDAPAuth
-        ldap_auth1 = LDAPAuth(server_uri=server_uri, base_dn=base_dn)
-        self.config = self.TestConfig(auth=[ldap_auth1, ], user_autocreate=True)
         handle_auth = self.request.handle_auth
 
         # tests that must not authenticate:
@@ -79,6 +90,16 @@
     slapd_config = SLAPD_CONFIG
     ldif_content = LDIF_CONTENT
 
+    class Config(wikiconfig.Config):
+        from MoinMoin.auth.ldap_login import LDAPAuth
+        from MoinMoin.auth import MoinAuth
+        server_uri = self.ldap_env.slapd.url # XXX no self
+        base_dn = self.ldap_env.basedn
+        ldap_auth = LDAPAuth(server_uri=server_uri, base_dn=base_dn)
+        moin_auth = MoinAuth()
+        auth = [ldap_auth, moin_auth]
+        user_autocreate = True
+
     def teardown_class(self):
         """ Stop slapd, remove LDAP server environment """
         #self.ldap_env.stop_slapd()  # it is already stopped
@@ -89,14 +110,6 @@
             a default password there), then try logging in via moin login using
             that default password or an empty password.
         """
-        server_uri = self.ldap_env.slapd.url
-        base_dn = self.ldap_env.basedn
-
-        from MoinMoin.auth.ldap_login import LDAPAuth
-        ldap_auth = LDAPAuth(server_uri=server_uri, base_dn=base_dn)
-        from MoinMoin.auth import MoinAuth
-        moin_auth = MoinAuth()
-        self.config = self.TestConfig(auth=[ldap_auth, moin_auth], user_autocreate=True)
 
         nuke_user(self.request, u'usera')
 
@@ -171,6 +184,18 @@
     slapd_config = SLAPD_CONFIG
     ldif_content = LDIF_CONTENT
 
+    class Config(wikiconfig.Config):
+        from MoinMoin.auth.ldap_login import LDAPAuth
+        authlist = []
+        for ldap_env in self.ldap_envs: # XXX no self
+            server_uri = ldap_env.slapd.url
+            base_dn = ldap_env.basedn
+            ldap_auth = LDAPAuth(server_uri=server_uri, base_dn=base_dn,
+                                 timeout=1) # short timeout, faster testing
+            authlist.append(ldap_auth)
+        auth = authlist
+        user_autocreate = True
+
     def setup_class(self):
         """ Create LDAP servers environment, start slapds """
         self.ldap_envs = []
@@ -195,16 +220,6 @@
 
     def testMoinLDAPFailOver(self):
         """ Try if it does a failover to a secondary LDAP, if the primary fails. """
-        from MoinMoin.auth.ldap_login import LDAPAuth
-        authlist = []
-        for ldap_env in self.ldap_envs:
-            server_uri = ldap_env.slapd.url
-            base_dn = ldap_env.basedn
-            ldap_auth = LDAPAuth(server_uri=server_uri, base_dn=base_dn,
-                                 timeout=1) # short timeout, faster testing
-            authlist.append(ldap_auth)
-
-        self.config = self.TestConfig(auth=authlist, user_autocreate=True)
         handle_auth = self.request.handle_auth
 
         # authenticate user (with primary slapd):
--- a/MoinMoin/config/_tests/test_configs.py	Thu Aug 21 16:34:39 2008 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
-from MoinMoin.config.multiconfig import DefaultConfig
-
-class NoUnderlay(DefaultConfig):
-    data_underlay_dir = None
-
-_tests = [NoUnderlay, ]
-
-class TestConfigs:
-    def testConfigs(self):
-        for cls in _tests:
-            cls.data_dir = self.request.cfg.data_dir
-            cls.secrets = self.request.cfg.secrets
-            # quite a bad hack to make _importPlugin succeed
-            cls('MoinMoin')
--- a/MoinMoin/config/multiconfig.py	Thu Aug 21 16:34:39 2008 +0900
+++ b/MoinMoin/config/multiconfig.py	Tue Aug 26 10:25:31 2008 +0900
@@ -591,11 +591,12 @@
                             # Load the module and set in sys.modules
                             module = imp.load_module(modname, fp, path, info)
                             setattr(sys.modules[self.siteid], 'csum', module)
-                            self._plugin_modules.append(modname)
                         finally:
                             # Make sure fp is closed properly
                             if fp:
                                 fp.close()
+                    if modname not in self._plugin_modules:
+                        self._plugin_modules.append(modname)
             finally:
                 imp.release_lock()
         except ImportError, err:
--- a/MoinMoin/conftest.py	Thu Aug 21 16:34:39 2008 +0900
+++ b/MoinMoin/conftest.py	Tue Aug 26 10:25:31 2008 +0900
@@ -12,34 +12,28 @@
 classes by the framework.
 
 Tests that require a certain configuration, like section_numbers = 1, must
-use a TestConfig to create the required configuration before the test.
-Deleting the TestConfig instance will restore the previous configuration.
+use a Config class to define the required configuration within the test class.
 
-@copyright: 2005 Nir Soffer, 2007 Alexander Schremmer
+@copyright: 2005 MoinMoin:NirSoffer,
+            2007 MoinMoin:AlexanderSchremmer,
+            2008 MoinMoin:ThomasWaldmann
 @license: GNU GPL, see COPYING for details.
 """
 
 import atexit
-from sys import modules
 import sys
 
 import py
 
-
 rootdir = py.magic.autopath().dirpath()
 moindir = rootdir.join("..")
-
 sys.path.insert(0, str(moindir))
-from MoinMoin._tests import maketestwiki, compat
-modules["unittest"] = compat # evil hack
-
-wikiconfig_dir = str(moindir.join("tests"))
 
 from MoinMoin.support.python_compatibility import set
+from MoinMoin._tests import maketestwiki, wikiconfig
 
 coverage_modules = set()
 
-
 try:
     """
     This code adds support for coverage.py (see
@@ -52,7 +46,7 @@
 
     def report_coverage():
         coverage.stop()
-        module_list = [modules[mod] for mod in coverage_modules]
+        module_list = [sys.modules[mod] for mod in coverage_modules]
         module_list.sort()
         coverage.report(module_list)
 
@@ -70,17 +64,14 @@
     coverage = None
 
 
-def init_test_request(static_state=[False]):
+def init_test_request(given_config=None, static_state=[False]):
     from MoinMoin.request import request_cli
     from MoinMoin.user import User
     from MoinMoin.formatter.text_html import Formatter as HtmlFormatter
     if not static_state[0]:
         maketestwiki.run(True)
         static_state[0] = True
-    if sys.path[0] != wikiconfig_dir:
-        sys.path.insert(0, wikiconfig_dir) # this is a race with py.test's collectors
-                                           # because they modify sys.path as well
-    request = request_cli.Request()
+    request = request_cli.Request(given_config=given_config)
     request.form = request.args = request.setup_args()
     request.user = User(request)
     request.html_formatter = HtmlFormatter(request)
@@ -88,84 +79,6 @@
     return request
 
 
-class TestConfig:
-    """ Custom configuration for unit tests
-
-    Some tests assume a specific configuration, and will fail if the wiki admin
-    changed the configuration. For example, DateTime macro test assume
-    the default datetime_fmt.
-
-    When you set custom values in a TestConfig, the previous values are saved,
-    and when the TestConfig is called specifically, they are restored automatically.
-
-    Typical Usage
-    -------------
-    ::
-        class SomeTest:
-            def setUp(self):
-                self.config = self.TestConfig(defaults=key_list, key=value,...)
-            def tearDown(self):
-                self.config.restore()
-            def testSomething(self):
-                # test that needs those defaults and custom values
-    """
-
-    def __init__(self, request):
-        """ Create temporary configuration for a test
-
-        @param request: current request
-        """
-        self.request = request
-        self.old = {}  # Old config values
-        self.new = []  # New added attributes
-
-    def __call__(self, defaults=(), **custom):
-        """ Initialise a temporary configuration for a test
-
-        @param defaults: list of keys that should use the default value
-        @param custom: other keys using non default values, or new keys
-               that request.cfg does not have already
-        """
-        self.setDefaults(defaults)
-        self.setCustom(**custom)
-
-        return self
-
-    def setDefaults(self, defaults=()):
-        """ Set default values for keys in defaults list
-
-        Non existing default will raise an AttributeError.
-        """
-        from MoinMoin.config import multiconfig
-        for key in defaults:
-            self._setattr(key, getattr(multiconfig.DefaultConfig, key))
-
-    def setCustom(self, **custom):
-        """ Set custom values """
-        for key, value in custom.items():
-            self._setattr(key, value)
-
-    def _setattr(self, key, value):
-        """ Set a new value for key saving new added keys """
-        if hasattr(self.request.cfg, key):
-            self.old[key] = getattr(self.request.cfg, key)
-        else:
-            self.new.append(key)
-        setattr(self.request.cfg, key, value)
-
-    def restore(self):
-        """ Restore previous request.cfg
-
-        Set old keys to old values and delete new keys.
-        """
-        for key, value in self.old.items():
-            setattr(self.request.cfg, key, value)
-        for key in self.new:
-            delattr(self.request.cfg, key)
-    __del__ = restore # XXX __del__ semantics are currently broken
-
-
-
 # py.test customization starts here
 
 class MoinTestFunction(py.test.collect.Function):
@@ -183,8 +96,10 @@
 
     def setup(self):
         cls = self.obj
-        cls.request = self.parent.request
-        cls.TestConfig = TestConfig(cls.request)
+        if hasattr(cls, 'Config'):
+            cls.request = init_test_request(given_config=cls.Config)
+        else:
+            cls.request = self.parent.request
         super(MoinClassCollector, self).setup()
 
 
@@ -193,7 +108,7 @@
     Function = MoinTestFunction
 
     def __init__(self, *args, **kwargs):
-        self.request = init_test_request()
+        self.request = init_test_request(given_config=wikiconfig.Config)
         super(Module, self).__init__(*args, **kwargs)
 
     def run(self, *args, **kwargs):
--- a/MoinMoin/converter/_tests/test_text_html_text_moin_wiki.py	Thu Aug 21 16:34:39 2008 +0900
+++ b/MoinMoin/converter/_tests/test_text_html_text_moin_wiki.py	Tue Aug 26 10:25:31 2008 +0900
@@ -23,12 +23,6 @@
 
 class TestBase(object):
 
-    def setup_method(self, method):
-        self.cfg = self.TestConfig(bang_meta=True)
-
-    def teardown_method(self, method):
-        del self.cfg
-
     def do_convert_real(self, func_args, successful=True):
         try:
             ret = convert(*func_args)
--- a/MoinMoin/parser/_tests/test_text_creole.py	Thu Aug 21 16:34:39 2008 +0900
+++ b/MoinMoin/parser/_tests/test_text_creole.py	Tue Aug 26 10:25:31 2008 +0900
@@ -83,15 +83,6 @@
 class TestHeadings(ParserTestCase):
     """ Test various heading problems """
 
-    def class_setup(self):
-        """ Require show_section_numbers = 0 to workaround counter
-        global state saved in request.
-        """
-        self.config = self.TestConfig(show_section_numbers=0)
-
-    def class_teardown(self):
-        del self.config
-
     def testIgnoreWhiteSpaceAroundHeadingText(self):
         """ parser.wiki: ignore white space around heading text
 
@@ -112,15 +103,6 @@
 
 class TestTOC(ParserTestCase):
 
-    def class_setup(self):
-        """ Require show_section_numbers = 0 to workaround counter
-        global state saved in request.
-        """
-        self.config = self.TestConfig(show_section_numbers=0)
-
-    def class_teardown(self):
-        del self.config
-
     def testHeadingWithWhiteSpace(self):
         """ parser.wiki: TOC links to headings with white space
 
--- a/MoinMoin/parser/_tests/test_text_moin_wiki.py	Thu Aug 21 16:34:39 2008 +0900
+++ b/MoinMoin/parser/_tests/test_text_moin_wiki.py	Tue Aug 26 10:25:31 2008 +0900
@@ -92,15 +92,6 @@
 class TestHeadings(ParserTestCase):
     """ Test various heading problems """
 
-    def class_setup(self):
-        """ Require show_section_numbers = 0 to workaround counter
-        global state saved in request.
-        """
-        self.config = self.TestConfig(show_section_numbers=0)
-
-    def class_teardown(self):
-        del self.config
-
     def testIgnoreWhiteSpaceAroundHeadingText(self):
         """ parser.wiki: ignore white space around heading text
 
@@ -121,15 +112,6 @@
 
 class TestTOC(ParserTestCase):
 
-    def class_setup(self):
-        """ Require show_section_numbers = 0 to workaround counter
-        global state saved in request.
-        """
-        self.config = self.TestConfig(show_section_numbers=0)
-
-    def class_teardown(self):
-        del self.config
-
     def testHeadingWithWhiteSpace(self):
         """ parser.wiki: TOC links to headings with white space
 
@@ -182,13 +164,6 @@
         (u'<<DateTime(1970-01-06T00:00:00)>>',   '1970-01-06 00:00:00'), # fails e.g. for Europe/Vilnius
         )
 
-    def class_setup(self):
-        """ Require default date and time format config values """
-        self.config = self.TestConfig(defaults=('date_fmt', 'datetime_fmt'))
-
-    def class_teardown(self):
-        del self.config
-
     def testDateTimeMacro(self):
         """ parser.wiki: DateTime macro """
         note = """
--- a/MoinMoin/request/__init__.py	Thu Aug 21 16:34:39 2008 +0900
+++ b/MoinMoin/request/__init__.py	Tue Aug 26 10:25:31 2008 +0900
@@ -112,7 +112,7 @@
     proxy_host = 'x-forwarded-host' # original host: header as seen by the proxy (e.g. wiki.example.org)
     proxy_xff = 'x-forwarded-for' # list of original remote_addrs as seen by the proxies (e.g. <clientip>,<proxy1>,<proxy2>,...)
 
-    def __init__(self, properties={}):
+    def __init__(self, properties={}, given_config=None):
 
         # twistd's daemonize() overrides our umask, so we reset it here every
         # request. we do it for all request types to avoid similar problems.
@@ -163,7 +163,7 @@
             # order is important here!
             self.__dict__.update(properties)
             try:
-                self._load_multi_cfg()
+                self._load_multi_cfg(given_config)
             except error.NoConfigMatchedError:
                 self.makeForbidden(404, 'No wiki configuration matching the URL found!\r\n')
                 return
@@ -354,12 +354,15 @@
 
     dicts = property(getDicts, None, delDicts)
 
-    def _load_multi_cfg(self):
+    def _load_multi_cfg(self, given_config=None):
         # protect against calling multiple times
         if not hasattr(self, 'cfg'):
-            self.clock.start('load_multi_cfg')
-            self.cfg = multiconfig.getConfig(self.url)
-            self.clock.stop('load_multi_cfg')
+            if given_config is None:
+                self.clock.start('load_multi_cfg')
+                self.cfg = multiconfig.getConfig(self.url)
+                self.clock.stop('load_multi_cfg')
+            else:
+                self.cfg = given_config('MoinMoin._tests.wikiconfig') # used for tests' TestConfig
 
     def setAcceptedCharsets(self, accept_charset):
         """ Set accepted_charsets by parsing accept-charset header
--- a/MoinMoin/request/_tests/test_request.py	Thu Aug 21 16:34:39 2008 +0900
+++ b/MoinMoin/request/_tests/test_request.py	Tue Aug 26 10:25:31 2008 +0900
@@ -10,6 +10,7 @@
 import py
 
 from MoinMoin import config, wikiutil
+from MoinMoin._tests import wikiconfig
 
 from MoinMoin.request import HeadersAlreadySentException
 
@@ -73,12 +74,6 @@
 
 class TestGroupPages(object):
 
-    def setup_method(self, method):
-        self.config = self.TestConfig(page_group_regex=r'.+Group')
-
-    def teardown_method(self, method):
-        del self.config
-
     def testNormalizeGroupName(self):
         """ request: normalize pagename: restrict groups to alpha numeric Unicode
 
--- a/MoinMoin/request/request_cli.py	Thu Aug 21 16:34:39 2008 +0900
+++ b/MoinMoin/request/request_cli.py	Tue Aug 26 10:25:31 2008 +0900
@@ -16,7 +16,7 @@
 class Request(RequestBase):
     """ specialized on command line interface and script requests """
 
-    def __init__(self, url='CLI', pagename='', properties={}):
+    def __init__(self, url='CLI', pagename='', properties={}, given_config=None):
         self.saved_cookie = ''
         self.path_info = '/' + pagename
         self.query_string = ''
@@ -32,7 +32,7 @@
         self.content_length = None
         self.if_modified_since = None
         self.if_none_match = None
-        RequestBase.__init__(self, properties)
+        RequestBase.__init__(self, properties, given_config)
         self.cfg.caching_formats = [] # don't spoil the cache
         self.initTheme() # usually request.run() does this, but we don't use it
 
--- a/MoinMoin/security/_tests/test_security.py	Thu Aug 21 16:34:39 2008 +0900
+++ b/MoinMoin/security/_tests/test_security.py	Tue Aug 26 10:25:31 2008 +0900
@@ -23,11 +23,6 @@
 
 class TestACLStringIterator(object):
 
-    def setup_method(self, method):
-        self.config = self.TestConfig(defaults=['acl_rights_valid', 'acl_rights_before'])
-    def teardown_method(self, method):
-        del self.config
-
     def testEmpty(self):
         """ security: empty acl string raise StopIteration """
         iter = acliter(self.request.cfg.acl_rights_valid, '')
@@ -192,13 +187,11 @@
     """
     def setup_method(self, method):
         # Backup user
-        self.config = self.TestConfig(defaults=['acl_rights_valid', 'acl_rights_before'])
         self.savedUser = self.request.user.name
 
     def teardown_method(self, method):
         # Restore user
         self.request.user.name = self.savedUser
-        del self.config
 
     def testApplyACLByUser(self):
         """ security: applying acl by user name"""
@@ -250,9 +243,6 @@
 class TestPageAcls(object):
     """ security: real-life access control list on pages testing
     """
-    acls_before = u"WikiAdmin:admin,read,write,delete,revert"
-    acls_default = u"All:read,write"
-    acls_after = u"All:read"
     mainpage_name = u'AclTestMainPage'
     subpage_name = u'AclTestMainPage/SubPage'
     pages = [
@@ -261,20 +251,14 @@
         (subpage_name, u"FooFoo!"),
     ]
 
+    from MoinMoin._tests import wikiconfig
+    class Config(wikiconfig.Config):
+        acl_rights_before = u"WikiAdmin:admin,read,write,delete,revert"
+        acl_rights_default = u"All:read,write"
+        acl_rights_after = u"All:read"
+        acl_hierarchic = False
+
     def setup_class(self):
-        self.config = self.TestConfig(
-            acl_rights_before=self.acls_before,
-            acl_rights_default=self.acls_default,
-            acl_rights_after=self.acls_after,
-            acl_hierarchic=False,
-            defaults=['acl_rights_valid'])
-        # TestConfig is crap, it does some wild hack and does not inherit from DefaultConfig
-        # nor call DefaultConfig's __init__() to do post processing, thus we do it here for now:
-        cfg = self.request.cfg
-        cfg.cache.acl_rights_before = AccessControlList(cfg, [cfg.acl_rights_before])
-        cfg.cache.acl_rights_default = AccessControlList(cfg, [cfg.acl_rights_default])
-        cfg.cache.acl_rights_after = AccessControlList(cfg, [cfg.acl_rights_after])
-
         # Backup user
         self.savedUser = self.request.user.name
         self.request.user = User(self.request, auth_username=u'WikiAdmin')
@@ -284,12 +268,6 @@
             create_page(self.request, page_name, page_content)
 
     def teardown_class(self):
-        del self.config
-        cfg = self.request.cfg
-        cfg.cache.acl_rights_before = AccessControlList(cfg, [cfg.acl_rights_before])
-        cfg.cache.acl_rights_default = AccessControlList(cfg, [cfg.acl_rights_default])
-        cfg.cache.acl_rights_after = AccessControlList(cfg, [cfg.acl_rights_after])
-
         # Restore user
         self.request.user.name = self.savedUser
 
--- a/MoinMoin/util/_tests/test_pysupport.py	Thu Aug 21 16:34:39 2008 +0900
+++ b/MoinMoin/util/_tests/test_pysupport.py	Tue Aug 26 10:25:31 2008 +0900
@@ -105,7 +105,7 @@
         """ Create test plugin, skiping if plugin exists """
         if self.pluginExists():
             self.shouldDeleteTestPlugin = False
-            py.test.skip("Won't overwrite exiting plugin: %s" % self.plugin)
+            py.test.skip("Won't overwrite existing plugin: %s" % self.plugin)
         self.key = random_string(32, 'abcdefg')
         data = '''
 # If you find this file in your wiki plugin directory, you can safely
--- a/MoinMoin/wikiutil.py	Thu Aug 21 16:34:39 2008 +0900
+++ b/MoinMoin/wikiutil.py	Tue Aug 26 10:25:31 2008 +0900
@@ -1128,7 +1128,7 @@
     See importPlugin docstring.
     """
     if not name in builtinPlugins(kind):
-        raise PluginMissingError
+        raise PluginMissingError()
     moduleName = 'MoinMoin.%s.%s' % (kind, name)
     return importNameFromPlugin(moduleName, function)
 
@@ -1180,24 +1180,22 @@
     """
     # short-cut if we've loaded the dict already
     # (or already failed to load it)
-    if kind in cfg._site_plugin_lists:
-        return cfg._site_plugin_lists[kind]
-
-    result = {}
-
-    for modname in cfg._plugin_modules:
-        try:
-            module = pysupport.importName(modname, kind)
-            packagepath = os.path.dirname(module.__file__)
-            plugins = pysupport.getPluginModules(packagepath)
-
-            for p in plugins:
-                if not p in result:
-                    result[p] = '%s.%s' % (modname, kind)
-        except AttributeError:
-            pass
-
-    cfg._site_plugin_lists[kind] = result
+    cache = cfg._site_plugin_lists
+    if kind in cache:
+        result = cache[kind]
+    else:
+        result = {}
+        for modname in cfg._plugin_modules:
+            try:
+                module = pysupport.importName(modname, kind)
+                packagepath = os.path.dirname(module.__file__)
+                plugins = pysupport.getPluginModules(packagepath)
+                for p in plugins:
+                    if not p in result:
+                        result[p] = '%s.%s' % (modname, kind)
+            except AttributeError:
+                pass
+        cache[kind] = result
     return result
 
 
--- a/docs/CHANGES	Thu Aug 21 16:34:39 2008 +0900
+++ b/docs/CHANGES	Tue Aug 26 10:25:31 2008 +0900
@@ -72,13 +72,22 @@
     * backup action and associated settings
 
   New Features:
+    * New "modernized" theme.
+    * New macros "WikiConfig" and "WikiConfigHelp".
     * GUI Editor:
       * upgraded to use FCKEditor version 2.6.3 (current version)
       * user can insert and modify various types of MoinMoin links
-    * per-parser quickhelp, 'quickhelp' class variable of parser class
-
-
-Version 1.7.current:
+    * Per-parser quickhelp, 'quickhelp' class variable of parser class.
+    * New plugin_dirs setting to allow multiple plugin paths (additional to
+      the automatically configured plugin_dir [default: data_dir/plugin]).
+    * @EMAIL@ expands to a MailTo macro call with the obfuscated email address
+      of the current user.
+
+
+Version 1.7.1:
+  New features:
+    * New 'cache' action (see developer notes).
+
   Fixes:
     * Security fix: XSS fix for advanced search form
     * Avoid creation of new pagedirs with empty edit-log files by just
@@ -89,21 +98,52 @@
       that can be used to move trash pages into some trash/ directory and also
       moves deleted pages into some deleted/ directory. Maybe keep a copy of
       those directories for a while just for the case.
-    * Standalone server: fix --pidfile option
+    * Server specific fixes:
+      * standalone (wikiserver.py): fix --pidfile and --group option, fix
+        operation without a wikiserverconfig.py (use builtin defaults).
+      * mod_python: work around mod_python 3.3.1 problems with file uploads.
+        Note: if you are still using mod_python, we strongly recommend you
+	      try out mod_wsgi (in daemon mode) - it has less bugs, better
+	      security, better separation, WSGI is a Python standard, and moin
+	      developers also use WSGI. See HelpOnInstalling/ApacheWithModWSGI.
+    * revert action: fixed for deleted pages.
     * Search:
       * Xapian indexing: Removed crappy "hostname" tokenization.
         Fixes MoinMoinBugs/1.7 XapianNotWorkingWithLeadingNumbersInTitle.
+        Also tokenize CamelCase parts of non-wikiwords.
       * Make query parser reject more invalid input.
       * If query parsing raises a BracketError, at least tell what the problem
         is (and not just raise empty  ValueError).
-     * Category search: ignore traling whitespace after ----
+      * Category search: ignore traling whitespace after ----
     * Argument parser:
       * Fixed sort() usage in UnitArgument to be Python 2.3 compatible.
       * Fixed MoinMoinBugs/TypeErrorInWikiutils.
-    * TableOfContents macro: skip outer-most <ol> levels when page isn't using
-      the biggest headings
+    * Macros:
+      * TableOfContents: skip outer-most <ol> levels when page isn't using
+        the biggest headings
+      * MonthCalendar: fix MoinMoinBugs/MonthCalendarBreaksOnApostrophe
+    * xslt parser: fix MoinMoinBugs/DoNotConvertUnicodeToUTF8ForXsltParser
     * OpenID RP: make it compatible to python-openid 2.2.x
     * PackagePages.collectpackage: removed encoding from file name of zipfile
+    * Surge protection: exclude localnet no matter whether user is known or not.
+    * Notifications: fix MoinMoinBugs/DuplicateNewUserNotification
+    * Script moin account create/disable/resetpw: checks for already existing
+      user now.
+
+  Other changes:
+    * Prevent CategoryTemplate being listed as a category (it is a Template)
+      by changing the default page_category_regex.
+
+  Developer notes:
+    * New MoinMoin.action.cache - can be used to cache expensively rendered
+      output, e.g. generated images). Once put into the cache, moin can emit
+      a http response for that content very fast and very efficient (including
+      "304 not changed" handling.
+    * New file-like API in MoinMoin.caching (good for dealing with medium
+      to large files without consuming lots of memory).
+    * wikiutil.importPlugin supports getting the whole plugin module object
+      by giving function=None.
+
 
 Version 1.7.0:
   Note: This is a reduced CHANGES, ommitting details from rc/beta test and
--- a/tests/wikiconfig.py	Thu Aug 21 16:34:39 2008 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,28 +0,0 @@
-# -*- coding: iso-8859-1 -*-
-"""
-MoinMoin - test wiki configuration
-
-@copyright: 2000-2004 by Juergen Hermann <jh@web.de>
-@license: GNU GPL, see COPYING for details.
-"""
-
-import os
-
-from MoinMoin.config.multiconfig import DefaultConfig
-
-
-class Config(DefaultConfig):
-    sitename = u'Developer Test Wiki'
-    logo_string = sitename
-
-    _base_dir = os.path.join(os.path.dirname(__file__), 'wiki')
-    data_dir = os.path.join(_base_dir, "data")
-    data_underlay_dir = os.path.join(_base_dir, "underlay")
-
-    show_hosts = 1
-
-    secrets = 'some not secret string just to make tests happy'
-
-    # used to check if it is really a wiki we may modify
-    is_test_wiki = True
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/wiki/server/test.wsgi	Tue Aug 26 10:25:31 2008 +0900
@@ -0,0 +1,89 @@
+#!/usr/bin/env python
+"""
+A simple WSGI test application.
+
+Its main purpose is to show that WSGI support works (meaning that the
+web server and the WSGI adaptor / support module are configured correctly).
+
+As a nice plus, it outputs some interesting system / WSGI values as a nice
+HTML table.
+
+The main use of this script will be using the WSGI "application" defined
+below within your production WSGI environment. You will use some code similar
+to what you see at the end of this script to use the application from that
+environment. For the special case of apache2/mod_wsgi, it shoud be possible
+to directly use this file.
+
+If you start this script from the commandline either with python2.5 or with
+and older python + wsgiref module installed, it will serve the content on
+http://localhost:8000/ - this is mainly for debugging THIS script.
+
+@copyright: 2008 by MoinMoin:ThomasWaldmann
+@license: Python License, see LICENSE.Python for details.
+"""
+import os.path
+import os
+import sys
+
+try:
+    __file__
+except NameError:
+    __file__ = '?'
+
+html_template = """\
+<html>
+<head>
+ <title>WSGI Test Script</title>
+</head>
+<body>
+ <h1>WSGI test script is working!</h1>
+ <table border=1>
+  <tr><th colspan=2>1. System Information</th></tr>
+  <tr><td>Python</td><td>%(python_version)s</td></tr>
+  <tr><td>Python Path</td><td>%(python_path)s</td></tr>
+  <tr><td>Platform</td><td>%(platform)s</td></tr>
+  <tr><td>Absolute path of this script</td><td>%(abs_path)s</td></tr>
+  <tr><td>Filename</td><td>%(filename)s</td></tr>
+  <tr><th colspan=2>2. WSGI Environment</th></tr>
+%(wsgi_env)s
+ </table>
+</body>
+</html>
+"""
+
+row_template = "  <tr><td>%s</td><td>%r</td></tr>"
+
+
+def application(environ, start_response):
+    """ The WSGI test application """
+    # emit status / headers
+    status = "200 OK"
+    headers = [('Content-Type', 'text/html'), ]
+    start_response(status, headers)
+
+    # assemble and return content
+    content = html_template % {
+        'python_version': sys.version,
+        'platform': sys.platform,
+        'abs_path': os.path.abspath('.'),
+        'filename': __file__,
+        'python_path': repr(sys.path),
+        'wsgi_env': '\n'.join([row_template % item for item in environ.items()]),
+    }
+    return [content]
+
+
+if __name__ == '__main__':
+    # this runs when script is started directly from commandline
+    try:
+        # create a simple WSGI server and run the application
+        from wsgiref import simple_server
+        print "Running test application - point your browser at http://localhost:8000/ ..."
+        httpd = simple_server.WSGIServer(('', 8000), simple_server.WSGIRequestHandler)
+        httpd.set_app(application)
+        httpd.serve_forever()
+    except ImportError:
+        # wsgiref not installed, just output html to stdout
+        for content in application({}, lambda status, headers: None):
+            print content
+