Author: rjollos
Date: Mon Sep 30 05:31:52 2013
New Revision: 1527447
URL: http://svn.apache.org/r1527447
Log:
0.8dev: Added Bloodhound infrastructure to run functional test cases, and test
modules for prefs and ticket. Refs #387.
Patch by Olemis Lang.
Added:
bloodhound/trunk/bloodhound_multiproduct/tests/functional/
bloodhound/trunk/bloodhound_multiproduct/tests/functional/__init__.py
bloodhound/trunk/bloodhound_multiproduct/tests/functional/prefs.py
bloodhound/trunk/bloodhound_multiproduct/tests/functional/ticket.py
Modified:
bloodhound/trunk/trac/trac/tests/functional/__init__.py
Added: bloodhound/trunk/bloodhound_multiproduct/tests/functional/__init__.py
URL:
http://svn.apache.org/viewvc/bloodhound/trunk/bloodhound_multiproduct/tests/functional/__init__.py?rev=1527447&view=auto
==============================================================================
--- bloodhound/trunk/bloodhound_multiproduct/tests/functional/__init__.py
(added)
+++ bloodhound/trunk/bloodhound_multiproduct/tests/functional/__init__.py Mon
Sep 30 05:31:52 2013
@@ -0,0 +1,743 @@
+# -*- coding: utf-8 -*-
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+import imp
+from inspect import isclass
+import os
+from subprocess import call, Popen
+import sys
+import time
+import urllib
+import urllib2
+
+from trac.tests.contentgen import random_sentence, random_page,\
+ random_unique_camel
+from trac.tests import functional
+from trac.tests.functional.svntestenv import SvnFunctionalTestEnvironment
+from trac.tests.functional.testenv import FunctionalTestEnvironment,
ConnectError
+from trac.tests.functional.tester import b, FunctionalTester, internal_error,
tc
+from trac.util.compat import close_fds
+from trac.util.text import unicode_quote
+from trac.web.href import Href
+from multiproduct.api import MultiProductSystem
+from multiproduct import hooks
+
+from tests import unittest
+
+#----------------
+# Product-aware classes for functional tests
+#----------------
+
+class MultiproductFunctionalMixin(object):
+ """Mixin class applying multi-product upgrade path upon a given
+ functional Trac test environment. Access to the global environment
+ is provided at testing time. In order to obtain a compatible test
+ environment for a given product @see: `product_test_env` method
+
+ @attention: This class must precede functional test environment class in
+ class declaration because it overrides some methods
+ """
+
+ def init(self):
+ """Determine the location of Trac source code
+ """
+ self.bhmp_upgrade = False
+ self.trac_src = os.path.realpath(os.path.join(
+ __import__('trac', []).__file__, '..' , '..'))
+ self.bh_src = os.path.realpath(os.path.join(
+ __import__('multiproduct', []).__file__, '..' , '..', '..'))
+ self.htdigest = os.path.join(self.dirname, "htdigest")
+ self.htdigest_realm = 'bloodhound'
+ print "\nFound Trac source: %s" \
+ "\nFound Bloodhound source: %s" % (self.trac_src, self.bh_src)
+ super(MultiproductFunctionalMixin, self).init()
+
+ def create(self):
+ """Create a new test environment.
+ This will set up Bloodhound and authentication by invoking installer
+ script, then call :meth:`create_repo`.
+ """
+ os.mkdir(self.dirname)
+ self.create_repo()
+
+ self._bloodhound_install()
+ if call([sys.executable,
+ os.path.join(self.trac_src, 'contrib', 'htpasswd.py'), "-c",
+ "-b", self.htpasswd, "admin", "admin"], close_fds=close_fds,
+ cwd=self.command_cwd):
+ raise Exception('Unable to setup admin password')
+ self.adduser('user')
+ self._tracadmin('permission', 'add', 'admin', 'TRAC_ADMIN')
+
+ # Setup Trac logging
+ env = self.get_trac_environment()
+ env.config.set('logging', 'log_type', 'file')
+ for component in self.get_enabled_components():
+ env.config.set('components', component, 'enabled')
+ env.config.save()
+ self.post_create(env)
+
+ def adduser_htpasswd(self, user):
+ """Add a user to the environment. The password will be set
+ in htpasswd file to the same as username.
+ """
+ return super(MultiproductFunctionalMixin, self).adduser(user)
+
+ def adduser_htdigest(self, user):
+ """Add a user to the environment. The password will be set
+ in htdigest file to the same as username.
+ """
+ if call([sys.executable, os.path.join(self.trac_src, 'contrib',
+ 'htdigest.py'), '-b', self.htdigest, self.htdigest_realm,
+ user, user], close_fds=close_fds, cwd=self.command_cwd):
+ raise Exception('Unable to setup password for user "%s"' % user)
+
+ adduser = adduser_htdigest
+
+ def get_env_href(self, user=None, prefix=None, envname=None):
+ """Default implementation just returning href object for global
+ environment and failing if product prefix is specified.
+ """
+ if envname not in ('trac', None):
+ raise LookupError('Unknown environment ' + repr(envname))
+ if prefix is not None:
+ self._fail_no_mp_setup()
+ parts = urllib2.urlparse.urlsplit(self.url)
+ if not user or user == 'anonymous':
+ return Href('%s://%s/' % (parts[0], parts[1]))
+ else:
+ return Href('%s://%s:%s@%s/' % (parts[0], user, user, parts[1]))
+
+ def get_enabled_components(self):
+ """Also enable Bloodhound multiproduct plugin.
+ """
+ return super(MultiproductFunctionalMixin,
self).get_enabled_components() + \
+ ['multiproduct.*']
+
+ def post_create(self, env):
+ self.getLogger = lambda : env.log
+
+ print "Created test environment: %s" % self.dirname
+
+ # Setup URL generation for product environments
+ self.get_env_href = self.configure_web_hooks()
+
+ super(MultiproductFunctionalMixin, self).post_create(env)
+
+ def _tracadmin(self, *args, **kwargs):
+ """Execute trac-admin command in product or (by default) global context
+ """
+ do_wait = kwargs.pop('wait', False)
+ product_id = kwargs.pop('product', None)
+ if product_id:
+ if self.bhmp_upgrade and \
+ args[0] not in ProductAdminModule.GLOBAL_COMMANDS:
+ args = ('product', 'admin', product_id) + args
+ elif not self.bhmp_upgrade:
+ self._fail_no_mp_setup()
+
+ super(MultiproductFunctionalMixin, self)._tracadmin(*args, **kwargs)
+ if do_wait: # Delay to ensure command executes and caches resets
+ time.sleep(5)
+
+ def start(self):
+ """Starts the webserver, and waits for it to come up.
+
+ Notice: Same as inherited method but without basic auth by default
+ """
+ if 'FIGLEAF' in os.environ:
+ exe = os.environ['FIGLEAF']
+ if ' ' in exe: # e.g. 'coverage run'
+ args = exe.split()
+ else:
+ args = [exe]
+ else:
+ args = [sys.executable]
+ options = ["--port=%s" % self.port, "-s", "--hostname=127.0.0.1"]
+ if 'TRAC_TEST_TRACD_OPTIONS' in os.environ:
+ options += os.environ['TRAC_TEST_TRACD_OPTIONS'].split()
+ args.append(os.path.join(self.trac_src, 'trac', 'web',
+ 'standalone.py'))
+ server = Popen(args + options + [self.tracdir],
+ stdout=functional.logfile, stderr=functional.logfile,
+ close_fds=close_fds,
+ cwd=self.command_cwd,
+ )
+ self.pid = server.pid
+ # Verify that the url is ok
+ timeout = 30
+ while timeout:
+ try:
+ tc.go(self.url)
+ break
+ except ConnectError:
+ time.sleep(1)
+ timeout -= 1
+ else:
+ raise Exception('Timed out waiting for server to start.')
+ tc.url(self.url)
+
+ def restart(self):
+ """Restarts the webserver"""
+ self.stop()
+ self.start()
+
+ # Reload components e.g. those in /plugins folder
+ from trac.loader import load_components
+
+ global_env = self.get_trac_environment()
+ plugins_dir = global_env.shared_plugins_dir
+ load_components(global_env, plugins_dir and (plugins_dir,))
+
+ def product_test_env(self, product_id):
+ """Functional test environment for product
+
+ @param product_id: target product prefix
+ @return: an object reusing resources in target functional test
+ environment to implement a compatible interface for
+ a given product environment
+ @raise LookupError: if there's no product for given prefix
+ """
+ raise NotImplementedError()
+
+ def configure_web_hooks(self):
+ """Setup web bootstrap_handlers and generation of product and global
+ base URLs for a given user
+
+ :return: a function used to generate base URL for product and
+ global environments . It will satisfy the following signature
+ `base_url(user=None, prefix=None, envname=None)` where::
+
+ @param user: username used to construct URLs for authenticated
+ requests
+ @param prefix: product prefix ; global environment selected
+ if missing
+ @param envname: environment name , useful in functional setup
+ running sibling Trac environments under
+ parent directory
+
+ Generated URLs must be consistent with web hooks configuration
+ @see: `_configure_web_hooks` method . By default `envname` is ignored
+ and product base URL will be at /products under URL namespace of the
+ global environment.
+ """
+ def _default_base_href(user=None, prefix=None, envname=None):
+ # TODO: Does not generate /login ? Should it ?
+ parts = urllib2.urlparse.urlsplit(self.url)
+ if not user or user == 'anonymous':
+ global_href = Href('%s://%s/' % (parts[0], parts[1]))
+ else:
+ global_href = Href('%s://%s:%s@%s/' %
+ (parts[0], user, user, parts[1]))
+ return global_href if not prefix \
+ else Href(global_href('products', prefix))
+
+ return _default_base_href
+
+ # Protected methods
+
+ def _bloodhound_install(self):
+ """Execute Bloodhound installer script
+ """
+ cwd = os.getcwdu()
+
+ try:
+ os.chdir(os.path.join(self.bh_src, 'installer'))
+ create_digest = imp.load_source('bloodhound_setup',
+ os.path.join(self.bh_src,
'installer',
+ 'createdigest.py'))
+ sys.modules['createdigest'] = create_digest
+ bhsetup = imp.load_source('bloodhound_setup',
+ os.path.join(self.bh_src, 'installer',
+ 'bloodhound_setup.py'))
+
+ #FIXME: Account manager's store will not work even after this
+ # Prepare installer for HTTP basic authentication store
+# bhsetup.ACCOUNTS_CONFIG['account-manager'].update(
+# {'htpasswd_file' : self.htpasswd,
+# 'password_store' : 'HtPasswdStore'})
+
+ # Enable timeline and roadmap views; needed in functional tests
+ bhsetup.BASE_CONFIG['mainnav'].update({'timeline': 'enabled',
+ 'roadmap': 'enabled'})
+
+ bhsetup = bhsetup.BloodhoundSetup({'project' : 'trac',
+ 'envsdir' : self.dirname})
+
+ # Do not perform Bloodhound-specific wiki upgrades
+ bhsetup.apply_bhwiki_upgrades = False
+
+ bhsetup.setup(adminuser='admin', adminpass='admin',
+ dbstring=self.dburi, default_product_prefix='test',
+ digestfile=self.htdigest, realm=self.htdigest_realm,
+ repo_type=self.repotype,
+ repo_path=self.repo_path_for_initenv(),
+ sourcedir=self.bh_src)
+
+ finally:
+ os.chdir(cwd)
+
+ def _fail_no_mp_setup(self):
+ raise EnvironmentError('Product admin executed before upgrade')
+
+ def _default_product(self, envname=None):
+ """Default product configured for a given environment
+
+ @raise LookupError: if no environment matching `envname` can be found
+ """
+ if envname not in ('trac', None):
+ raise LookupError('Unable to open environment ' + envname)
+ env = self.get_trac_environment()
+ return MultiProductSystem(env).default_product_prefix
+
+
+class BloodhoundFunctionalTester(FunctionalTester):
+ """Leverages Trac library of higher-level operations for interacting with
+ a fully featured Apache(TM) Bloodhound test environment.
+
+ Many things have changed in recent versions of Apache(TM) Bloodhound
+ user interface once theme and dashboard are both installed:
+
+ - 'New Ticket' link has been phased out in favor of 'More fields' link in
+ quick create ticket shortcut menu.
+ - New helper method `quick_create_ticket` has been added to create a
+ new (random) ticket via quick create ticket shortcut menu.
+ - 'logged in as user' label replaced by '<i class="icon-user"></i>user'
+ - By using account manager plugin a web form must be submitted to login
+ - As a consequence of default hooks new tickets in global scope are
+ always bound to default product
+ - Timeline module is disabled; frequently used along functional tests
+ - View Tickets renamed to Tickets pointing at dashboard
+ - Milestones `No date set` label replaced by `Unscheduled`
+ - There's no actual button to submit `propertyform` in new ticket page
+ - Different markup used to render ticket fields
+
+ Other notable differences not solved by this class (target test cases
+ should be rewritten?)
+
+ - Preferences link removed in Bloodhound UI
+ - There's no such thing like ticket preview in Bloodhound UI
+ - 'Create New Ticket' label in new ticket page replaced by 'New Ticket'
+
+ As a consequence some methods of Trac functional tester have to be updated.
+ """
+
+ def login(self, username):
+ """Login as the given user
+
+ Consider that 'logged in as user' label has been replaced by
+ '<i class="icon-user"></i>user'
+ """
+ #FIXME: Keep/remove this ?
+ #tc.add_auth("", self.url, username, username)
+ self.go_to_front()
+ tc.find("Login")
+ tc.follow("Login")
+
+ # Submit user + password via account manager login form
+ tc.formvalue('acctmgr_loginform', 'user', username)
+ tc.formvalue('acctmgr_loginform', 'password', username)
+ tc.submit()
+ self.go_to_front()
+
+ tc.find(r'<i class="icon-user"></i>\s*%s' % username)
+ tc.find("Logout")
+ tc.url(self.url)
+ tc.notfind(internal_error)
+
+ def _post_create_ticket(self):
+ """Look at the newly created ticket page after creating it
+ """
+ # we should be looking at the newly created ticket
+ tc.url(self.url + '/ticket/%s' % (self.ticketcount + 1))
+ # Increment self.ticketcount /after/ we've verified that the ticket
+ # was created so a failure does not trigger spurious later
+ # failures.
+ self.ticketcount += 1
+
+ # verify the ticket creation event shows up in the timeline
+ self.go_to_timeline()
+ tc.formvalue('prefs', 'ticket', True)
+ tc.submit()
+ tc.find('Ticket.*#%s.*created' % self.ticketcount)
+
+ def create_ticket(self, summary=None, info=None):
+ """Create a new (random) ticket in the test environment. Returns
+ the new ticket number.
+
+ :param summary:
+ may optionally be set to the desired summary
+ :param info:
+ may optionally be set to a dictionary of field value pairs for
+ populating the ticket. ``info['summary']`` overrides summary.
+
+ `summary` and `description` default to randomly-generated values.
+ """
+ # [BLOODHOUND] New Ticket => More fields (in create ticket menu)
+ self.go_to_newticket()
+
+ tc.notfind(internal_error)
+ if summary == None:
+ summary = random_sentence(4)
+ tc.formvalue('propertyform', 'field_summary', summary)
+ tc.formvalue('propertyform', 'field_description', random_page())
+ if info:
+ for field, value in info.items():
+ tc.formvalue('propertyform', 'field_%s' % field, value)
+
+ # [BLOODHOUND] no actual button to submit /newticket `propertyform`
+ tc.submit()
+
+ self._post_create_ticket()
+ return self.ticketcount
+
+ def create_report(self, title, query, description):
+ """Create a new report with the given title, query, and description
+ """
+ self.go_to_front()
+ # [BLOODHOUND] View Tickets renamed to Tickets pointing at dashboard
+ tc.follow('Tickets')
+ tc.notfind(internal_error)
+ tc.follow('Reports')
+ tc.notfind(internal_error)
+ tc.formvalue('create_report', 'action', 'new') # select new report form
+ tc.submit()
+ tc.find('New Report')
+ tc.notfind(internal_error)
+ tc.formvalue('edit_report', 'title', title)
+ tc.formvalue('edit_report', 'description', description)
+ tc.formvalue('edit_report', 'query', query)
+ tc.submit()
+ reportnum = b.get_url().split('/')[-1]
+ # TODO: verify the url is correct
+ # TODO: verify the report number is correct
+ # TODO: verify the report does not cause an internal error
+ # TODO: verify the title appears on the report list
+ return reportnum
+
+ def create_milestone(self, name=None, due=None):
+ """Creates the specified milestone, with a random name if none is
+ provided. Returns the name of the milestone.
+ """
+ if name == None:
+ name = random_unique_camel()
+ milestone_url = self.url + "/admin/ticket/milestones"
+ tc.go(milestone_url)
+ tc.url(milestone_url)
+ tc.formvalue('addmilestone', 'name', name)
+ if due:
+ # TODO: How should we deal with differences in date formats?
+ tc.formvalue('addmilestone', 'duedate', due)
+ tc.submit()
+ tc.notfind(internal_error)
+ tc.notfind('Milestone .* already exists')
+ tc.url(milestone_url)
+ tc.find(name)
+
+ # Make sure it's on the roadmap.
+ tc.follow('Roadmap')
+ tc.url(self.url + "/roadmap")
+ tc.find('Milestone:.*%s' % name)
+ tc.follow(name)
+ tc.url('%s/milestone/%s' % (self.url, unicode_quote(name)))
+ if not due:
+ # [BLOODHOUND] No date set => Unscheduled
+ tc.find('Unscheduled')
+
+ return name
+
+ def go_to_query(self):
+ """Surf to the custom query page.
+ """
+ self.go_to_front()
+ # [BLOODHOUND] View Tickets (reports list) => Tickets (dashboard)
+ tc.follow('^Tickets$')
+ tc.notfind(internal_error)
+ tc.url(self.url + '/dashboard')
+ tc.follow('Custom Query')
+ tc.url(self.url + '/query')
+
+ # Bloodhound functional tester extensions
+
+ def go_to_newticket(self):
+ self.go_to_front()
+
+ tc.follow('More fields')
+
+ def quick_create_ticket(self, summary=None, info=None):
+ """Create a new (random) ticket in the test environment via quick
+ create ticket shortcut. Returns the new ticket number.
+
+ :param summary:
+ may optionally be set to the desired summary
+ :param info:
+ may optionally be set to a dictionary of field value pairs for
+ populating the ticket. Fields are populated afterwards by
+ navigating to ticket page, thereby ``info['summary']``overrides
+ ``summary``.
+
+ `summary` and `description` default to randomly-generated values.
+ """
+ self.go_to_front()
+ tc.notfind(internal_error)
+
+ if summary == None:
+ summary = random_sentence(4)
+ tc.formvalue('qct-form', 'field_summary', summary)
+ tc.formvalue('qct-form', 'field_description', random_page())
+ self._post_create_ticket()
+
+ if info:
+ # Second pass to update ticket fields
+ tc.url(self.url + '/ticket/%s' % (self.ticketcount + 1))
+ tc.notfind(internal_error)
+ for field, value in info.items():
+ tc.formvalue('inplace-propertyform', 'field_%s' % field, value)
+ tc.submit('submit')
+
+ return self.ticketcount
+
+ def find_ticket_field(self, fieldname, fieldval):
+ """Assert that expected value (pattern) matches value in ticket view
+ """
+ tc.find(r'<td [^>]*\bid="vc-%s"[^>]*>\s*%s\s*</td>' % (fieldname,
fieldval))
+
+
+class BloodhoundGlobalEnvFunctionalTester(BloodhoundFunctionalTester):
+ """Library of higher-level operations for interacting with
+ a global Apache(TM) Bloodhound test environment enabled with automatic
+ redirects from global environment to resources in default product.
+
+ Many things have changed in recent versions of Apache(TM) Bloodhound
+ user interface once theme and dashboard are both installed. Beyond common
+ differences this functional tester also deals with :
+
+ - Tickets are created in default product context
+ - Admin panels for ticket fields are only accessible in default product
+ context
+ - Reports are created in default product context
+
+ As a consequence some methods of Trac functional tester have to be
+ executed in special ways.
+ """
+ def __init__(self, url, default_product_url=None):
+ super(BloodhoundGlobalEnvFunctionalTester, self).__init__(url)
+ self.default_product_url = default_product_url
+
+ class in_defaut_product(object):
+ """Context manager temporarily switching to default product URL
+ """
+ def __init__(self, tester):
+ self.tester = tester
+ self.global_url = None
+
+ def __enter__(self):
+ """Replace tester base URL with default product's URL
+ """
+ self.global_url = self.tester.url
+ self.tester.url = self.tester.default_product_url
+ return self.tester
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ """Restore tester URL poiting at global environment
+ """
+ self.tester.url = self.global_url
+
+ def _post_create_ticket(self):
+ """Look at the newly created ticket page after creating it
+ ... but in default product context ...
+ """
+ superobj = super(BloodhoundGlobalEnvFunctionalTester, self)
+ with self.in_defaut_product(self):
+ return superobj._post_create_ticket()
+
+ def create_milestone(self, name=None, due=None):
+ """Creates the specified milestone, with a random name if none is
+ provided. Returns the name of the milestone.
+
+ ... executed in default product context
+ """
+ superobj = super(BloodhoundGlobalEnvFunctionalTester, self)
+ with self.in_defaut_product(self):
+ return superobj.create_milestone(name, due)
+
+ def create_component(self, name=None, user=None):
+ """Creates the specified component, with a random camel-cased name if
+ none is provided. Returns the name.
+
+ ... executed in default product context
+ """
+ superobj = super(BloodhoundGlobalEnvFunctionalTester, self)
+ with self.in_defaut_product(self):
+ return superobj.create_component(name, user)
+
+ def create_enum(self, kind, name=None):
+ """Helper to create the specified enum (used for ``priority``,
+ ``severity``, etc). If no name is given, a unique random word is used.
+ The name is returned.
+
+ ... executed in default product context
+
+ """
+ superobj = super(BloodhoundGlobalEnvFunctionalTester, self)
+ with self.in_defaut_product(self):
+ return superobj.create_enum(kind, name)
+
+ def create_version(self, name=None, releasetime=None):
+ """Create a new version. The name defaults to a random camel-cased
+ word if not provided.
+
+ ... executed in default product context
+ """
+ superobj = super(BloodhoundGlobalEnvFunctionalTester, self)
+ with self.in_defaut_product(self):
+ return superobj.create_version(name, releasetime)
+
+
+#----------------
+# Product-aware functional setup
+#----------------
+
+class MultiproductFunctionalTestSuite(functional.FunctionalTestSuite):
+ """TestSuite that leverages a test fixture containing a
+ FunctionalTestEnvironment and a FunctionalTester by also
+ upgrading them to multi-product support after activating Bloodhound theme
+ and dashboard plugins.
+ """
+
+ class env_class(MultiproductFunctionalMixin,
+ functional.FunctionalTestSuite.env_class):
+ pass
+
+ tester_class = BloodhoundGlobalEnvFunctionalTester
+
+ def setUp(self, port=None):
+ print "Starting web server ..."
+ try:
+ functional.FunctionalTestSuite.setUp(self)
+ except:
+ # Ensure tracd process is killed on failure
+ print "Stopping web server...\n"
+ testenv = getattr(self, '_testenv', None)
+ if testenv:
+ testenv.stop()
+ raise
+ else:
+ prefix = self._testenv._default_product()
+ default_product_base = self._testenv.get_env_href(user=None,
+ prefix=prefix)
+ self._tester.default_product_url = default_product_base()
+ print "Started web server: %s" % self._testenv.url
+
+ def run(self, result):
+ """Setup the fixture (self.setUp), call .setFixture on all the tests,
+ and tear down the fixture (self.tearDown).
+
+ Ensure marked tests will be wrapped by specialized context manager in
+ order access default product URL namespace instead of global.
+ """
+ self.setUp()
+ if hasattr(self, 'fixture'):
+ for test in self._tests:
+ if hasattr(test, 'setFixture'):
+ test.setFixture(self.fixture)
+ # Override unittest loop
+ for test in self._tests:
+ if result.shouldStop:
+ break
+ if getattr(test, 'BH_IN_DEFAULT_PRODUCT', False) and \
+ hasattr(self._tester, 'in_defaut_product'):
+ with self._tester.in_defaut_product(self._tester):
+ test(result)
+ else:
+ test(result)
+ self.tearDown()
+ return result
+
+ def tearDown(self):
+ print "\nStopping web server...\n"
+ functional.FunctionalTestSuite.tearDown(self)
+
+# Mark some test cases to be run against default product
+import trac.ticket.tests.functional
+import trac.admin.tests.functional
+for mdl in (trac.ticket.tests.functional, trac.admin.tests.functional):
+ for attr in dir(mdl):
+ attr = getattr(mdl, attr)
+ if isclass(attr) and issubclass(attr,
+
functional.FunctionalTwillTestCaseSetup):
+ attr.BH_IN_DEFAULT_PRODUCT = True
+del attr, mdl
+
+def trac_functionalSuite(suite):
+ from trac.tests.functional import testcases
+ suite.addTest(testcases.RegressionTestRev6017())
+ suite.addTest(testcases.RegressionTestTicket3833a())
+ suite.addTest(testcases.RegressionTestTicket3833b())
+ suite.addTest(testcases.RegressionTestTicket3833c())
+ suite.addTest(testcases.RegressionTestTicket5572())
+ suite.addTest(testcases.RegressionTestTicket7209())
+ suite.addTest(testcases.RegressionTestTicket9880())
+ suite.addTest(testcases.ErrorPageValidation())
+ suite.addTest(testcases.RegressionTestTicket3663())
+
+ import trac.versioncontrol.tests
+ trac.versioncontrol.tests.functionalSuite(suite)
+
+ # import trac.ticket.tests
+ # trac.ticket.tests.functionalSuite(suite)
+ import tests.functional.ticket
+ tests.functional.ticket.functionalSuite(suite)
+
+ # import trac.prefs.tests
+ # trac.prefs.tests.functionalSuite(suite)
+ import tests.functional.prefs
+ tests.functional.prefs.functionalSuite(suite)
+
+ import trac.wiki.tests
+ trac.wiki.tests.functionalSuite(suite)
+ import trac.timeline.tests
+ trac.timeline.tests.functionalSuite(suite)
+ import trac.admin.tests
+ trac.admin.tests.functionalSuite(suite)
+ # The db tests should be last since the backup test occurs there.
+ import trac.db.tests
+ trac.db.tests.functionalSuite(suite)
+
+
+def functionalSuite():
+ suite = MultiproductFunctionalTestSuite()
+ return suite
+
+
+def test_suite():
+ suite = functionalSuite()
+
+ # TODO: Load Bloodhound-specific functional test cases
+
+ #from tests import TestLoader
+ # FIXME: Does this work for functional tests suite ?
+ # bhsuite = TestLoader().discover_package('tests.functional',
pattern='*.py')
+
+ trac_functionalSuite(suite)
+
+ return suite
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
Added: bloodhound/trunk/bloodhound_multiproduct/tests/functional/prefs.py
URL:
http://svn.apache.org/viewvc/bloodhound/trunk/bloodhound_multiproduct/tests/functional/prefs.py?rev=1527447&view=auto
==============================================================================
--- bloodhound/trunk/bloodhound_multiproduct/tests/functional/prefs.py (added)
+++ bloodhound/trunk/bloodhound_multiproduct/tests/functional/prefs.py Mon Sep
30 05:31:52 2013
@@ -0,0 +1,94 @@
+# -*- coding: utf-8 -*-
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+from trac.tests import functional
+from trac.tests.functional.tester import internal_error, tc
+
+#----------------
+# Functional test cases for preferences (rewritten)
+#----------------
+
+# TODO: These classes are almost a copycat of Trac's. Beware of license header
+
+class TestPreferences(functional.FunctionalTwillTestCaseSetup):
+ def runTest(self):
+ """Set preferences for admin user"""
+ prefs_url = self._tester.url + "/prefs"
+ # [BLOODHOUND] Preferences link removed
+ tc.follow('/prefs')
+ tc.url(prefs_url)
+ tc.notfind('Your preferences have been saved.')
+ tc.formvalue('userprefs', 'name', ' System Administrator ')
+ tc.formvalue('userprefs', 'email', ' [email protected] ')
+ tc.submit()
+ tc.find('Your preferences have been saved.')
+ tc.follow('Date & Time')
+ tc.url(prefs_url + '/datetime')
+ tc.formvalue('userprefs', 'tz', 'GMT -10:00')
+ tc.submit()
+ tc.find('Your preferences have been saved.')
+ tc.follow('General')
+ tc.url(prefs_url)
+ tc.notfind('Your preferences have been saved.')
+ tc.find('value="System Administrator"')
+ tc.find(r'value="admin@example\.com"')
+ tc.follow('Date & Time')
+ tc.url(prefs_url + '/datetime')
+ tc.find('GMT -10:00')
+
+
+class RegressionTestRev5785(functional.FunctionalTwillTestCaseSetup):
+ def runTest(self):
+ """Test for regression of the fix in r5785"""
+ prefs_url = self._tester.url + "/prefs"
+ # [BLOODHOUND] Preferences link removed
+ tc.follow('/prefs')
+ tc.url(prefs_url)
+ tc.follow('Logout')
+ tc.notfind(internal_error) # See [5785]
+ tc.follow('Login')
+
+
+class RegressionTestTicket5765(functional.FunctionalTwillTestCaseSetup):
+ def runTest(self):
+ """Test for regression of http://trac.edgewall.org/ticket/5765
+ Unable to turn off 'Enable access keys' in Preferences
+ """
+ self._tester.go_to_front()
+ # [BLOODHOUND] Preferences link removed
+ tc.follow('/prefs')
+ tc.follow('Keyboard Shortcuts')
+ tc.formvalue('userprefs', 'accesskeys', True)
+ tc.submit()
+ tc.find('name="accesskeys".*checked="checked"')
+ tc.formvalue('userprefs', 'accesskeys', False)
+ tc.submit()
+ tc.notfind('name="accesskeys".*checked="checked"')
+
+def functionalSuite(suite=None):
+ if not suite:
+ import tests.functional
+ suite = tests.functional.functionalSuite()
+
+ suite.addTest(TestPreferences())
+ suite.addTest(RegressionTestRev5785())
+ suite.addTest(RegressionTestTicket5765())
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='functionalSuite')
Added: bloodhound/trunk/bloodhound_multiproduct/tests/functional/ticket.py
URL:
http://svn.apache.org/viewvc/bloodhound/trunk/bloodhound_multiproduct/tests/functional/ticket.py?rev=1527447&view=auto
==============================================================================
--- bloodhound/trunk/bloodhound_multiproduct/tests/functional/ticket.py (added)
+++ bloodhound/trunk/bloodhound_multiproduct/tests/functional/ticket.py Mon Sep
30 05:31:52 2013
@@ -0,0 +1,415 @@
+# -*- coding: utf-8 -*-
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+"""Override a few functional tests for tickets.
+"""
+
+from urlparse import urlsplit
+
+from trac.ticket.tests.functional import *
+
+#----------------
+# Functional test cases for tickets (rewritten)
+#----------------
+
+# TODO: These classes are almost a copycat of Trac's. Beware of license header
+
+class TestTicketPreview(FunctionalTwillTestCaseSetup):
+ """There's no such thing like ticket preview in Bloodhound but, if it would
+ then the corresponding Trac test case should be rewritten like this.
+ """
+ def runTest(self):
+ """Preview ticket creation
+ """
+ # [BLOODHOUND] New Ticket => More fields (in create ticket menu)
+ self._tester.go_to_newticket()
+
+ summary = random_sentence(5)
+ desc = random_sentence(5)
+ tc.formvalue('propertyform', 'field-summary', summary)
+ tc.formvalue('propertyform', 'field-description', desc)
+ tc.submit('preview')
+ tc.url(self._tester.url + '/newticket$')
+ tc.find('ticket not yet created')
+ tc.find(summary)
+ tc.find(desc)
+
+
+class TestTicketNoSummary(FunctionalTwillTestCaseSetup):
+ def runTest(self):
+ """Creating a ticket without summary should fail
+ """
+ # [BLOODHOUND] New Ticket => More fields (in create ticket menu)
+ self._tester.go_to_newticket()
+
+ desc = random_sentence(5)
+ tc.formvalue('propertyform', 'field-description', desc)
+ # [BLOODHOUND] no actual button to submit /newticket `propertyform`
+ tc.submit()
+ tc.find(desc)
+ tc.find('Tickets must contain a summary.')
+ # [BLOODHOUND] Create New Ticket => New Ticket
+ tc.find('New Ticket')
+ tc.find('ticket not yet created')
+
+
+class TestTicketCustomFieldTextNoFormat(FunctionalTwillTestCaseSetup):
+ def runTest(self):
+ """Test custom text field with no format explicitly specified.
+ Its contents should be rendered as plain text.
+ """
+ env = self._testenv.get_trac_environment()
+ env.config.set('ticket-custom', 'newfield', 'text')
+ env.config.set('ticket-custom', 'newfield.label',
+ 'Another Custom Field')
+ env.config.set('ticket-custom', 'newfield.format', '')
+ env.config.save()
+
+ self._testenv.restart()
+ val = "%s %s" % (random_unique_camel(), random_word())
+ ticketid = self._tester.create_ticket(summary=random_sentence(3),
+ info={'newfield': val})
+ self._tester.go_to_ticket(ticketid)
+
+ # [BLOODHOUND] Different markup to render field values
+ self._tester.find_ticket_field('newfield', val)
+
+
+class TestTicketCustomFieldTextAreaNoFormat(FunctionalTwillTestCaseSetup):
+ def runTest(self):
+ """Test custom textarea field with no format explicitly specified,
+ its contents should be rendered as plain text.
+ """
+ env = self._testenv.get_trac_environment()
+ env.config.set('ticket-custom', 'newfield', 'textarea')
+ env.config.set('ticket-custom', 'newfield.label',
+ 'Another Custom Field')
+ env.config.set('ticket-custom', 'newfield.format', '')
+ env.config.save()
+
+ self._testenv.restart()
+ val = "%s %s" % (random_unique_camel(), random_word())
+ ticketid = self._tester.create_ticket(summary=random_sentence(3),
+ info={'newfield': val})
+ self._tester.go_to_ticket(ticketid)
+
+ # [BLOODHOUND] Different markup to render field values
+ self._tester.find_ticket_field('newfield', val)
+
+
+class TestTicketCustomFieldTextWikiFormat(FunctionalTwillTestCaseSetup):
+ def runTest(self):
+ """Test custom text field with `wiki` format.
+ Its contents should through the wiki engine, wiki-links and all.
+ Feature added in http://trac.edgewall.org/ticket/1791
+ """
+ env = self._testenv.get_trac_environment()
+ env.config.set('ticket-custom', 'newfield', 'text')
+ env.config.set('ticket-custom', 'newfield.label',
+ 'Another Custom Field')
+ env.config.set('ticket-custom', 'newfield.format', 'wiki')
+ env.config.save()
+
+ self._testenv.restart()
+ word1 = random_unique_camel()
+ word2 = random_word()
+ val = "%s %s" % (word1, word2)
+ ticketid = self._tester.create_ticket(summary=random_sentence(3),
+ info={'newfield': val})
+ self._tester.go_to_ticket(ticketid)
+ wiki = '<a [^>]*>%s\??</a> %s' % (word1, word2)
+
+ # [BLOODHOUND] Different markup to render field values
+ self._tester.find_ticket_field('newfield', wiki)
+
+
+class TestTicketCustomFieldTextAreaWikiFormat(FunctionalTwillTestCaseSetup):
+ def runTest(self):
+ """Test custom textarea field with no format explicitly specified,
+ its contents should be rendered as plain text.
+ """
+ env = self._testenv.get_trac_environment()
+ env.config.set('ticket-custom', 'newfield', 'textarea')
+ env.config.set('ticket-custom', 'newfield.label',
+ 'Another Custom Field')
+ env.config.set('ticket-custom', 'newfield.format', 'wiki')
+ env.config.save()
+
+ self._testenv.restart()
+ word1 = random_unique_camel()
+ word2 = random_word()
+ val = "%s %s" % (word1, word2)
+ ticketid = self._tester.create_ticket(summary=random_sentence(3),
+ info={'newfield': val})
+ self._tester.go_to_ticket(ticketid)
+ wiki = '<p>\s*<a [^>]*>%s\??</a> %s<br />\s*</p>' % (word1, word2)
+
+ # [BLOODHOUND] Different markup to render field values
+ self._tester.find_ticket_field('newfield', wiki)
+
+
+class TestTicketCustomFieldTextReferenceFormat(FunctionalTwillTestCaseSetup):
+ # Run this test case in default product context to keep body agnostic to
+ # context switching
+ BH_IN_DEFAULT_PRODUCT = True
+
+ def runTest(self):
+ """Test custom text field with `reference` format.
+ Its contents are treated as a single value
+ and are rendered as an auto-query link.
+ Feature added in http://trac.edgewall.org/ticket/10643
+ """
+ env = self._testenv.get_trac_environment()
+ env.config.set('ticket-custom', 'newfield', 'text')
+ env.config.set('ticket-custom', 'newfield.label',
+ 'Another Custom Field')
+ env.config.set('ticket-custom', 'newfield.format', 'reference')
+ env.config.save()
+
+ self._testenv.restart()
+ word1 = random_unique_camel()
+ word2 = random_word()
+ val = "%s %s" % (word1, word2)
+ ticketid = self._tester.create_ticket(summary=random_sentence(3),
+ info={'newfield': val})
+ self._tester.go_to_ticket(ticketid)
+ query = 'status=!closed&newfield=%s\+%s' % (word1, word2)
+
+ path_prefix = urlsplit(self._tester.url).path
+ querylink = '<a href="%s/query\?%s">%s</a>' % (path_prefix, query, val)
+
+ # [BLOODHOUND] Different markup to render field values
+ self._tester.find_ticket_field('newfield', querylink)
+
+
+class TestTicketCustomFieldTextListFormat(FunctionalTwillTestCaseSetup):
+ # Run this test case in default product context to keep body agnostic to
+ # context switching
+ BH_IN_DEFAULT_PRODUCT = True
+
+ def runTest(self):
+ """Test custom text field with `list` format.
+ Its contents are treated as a space-separated list of values
+ and are rendered as separate auto-query links per word.
+ Feature added in http://trac.edgewall.org/ticket/10643
+ """
+ env = self._testenv.get_trac_environment()
+ env.config.set('ticket-custom', 'newfield', 'text')
+ env.config.set('ticket-custom', 'newfield.label',
+ 'Another Custom Field')
+ env.config.set('ticket-custom', 'newfield.format', 'list')
+ env.config.save()
+
+ self._testenv.restart()
+ word1 = random_unique_camel()
+ word2 = random_word()
+ val = "%s %s" % (word1, word2)
+ ticketid = self._tester.create_ticket(summary=random_sentence(3),
+ info={'newfield': val})
+ self._tester.go_to_ticket(ticketid)
+ query1 = 'status=!closed&newfield=~%s' % word1
+ query2 = 'status=!closed&newfield=~%s' % word2
+
+ path_prefix = urlsplit(self._tester.url).path
+ querylink1 = '<a href="%s/query\?%s">%s</a>' % (path_prefix,
+ query1, word1)
+ querylink2 = '<a href="%s/query\?%s">%s</a>' % (path_prefix,
+ query2, word2)
+ querylinks = '%s %s' % (querylink1, querylink2)
+
+ # [BLOODHOUND] Different markup to render field values
+ self._tester.find_ticket_field('newfield', querylinks)
+
+
+class RegressionTestTicket10828(FunctionalTwillTestCaseSetup):
+ # Run this test case in default product context to keep body agnostic to
+ # context switching
+ BH_IN_DEFAULT_PRODUCT = True
+
+ def runTest(self):
+ """Test for regression of http://trac.edgewall.org/ticket/10828
+ Rendered property changes should be described as lists of added and
+ removed items, even in the presence of comma and semicolon separators.
+ """
+ env = self._testenv.get_trac_environment()
+ env.config.set('ticket-custom', 'newfield', 'text')
+ env.config.set('ticket-custom', 'newfield.label',
+ 'A Custom Field')
+ env.config.set('ticket-custom', 'newfield.format', 'list')
+ env.config.save()
+
+ self._testenv.restart()
+ ticketid = self._tester.create_ticket(summary=random_sentence(3))
+ self._tester.go_to_ticket(ticketid)
+
+ word1 = random_unique_camel()
+ word2 = random_word()
+ val = "%s %s" % (word1, word2)
+ tc.formvalue('propertyform', 'field-newfield', val)
+ tc.submit('submit')
+ tc.find('<em>%s</em> <em>%s</em> added' % (word1, word2))
+
+ word3 = random_unique_camel()
+ word4 = random_unique_camel()
+ val = "%s, %s; %s" % (word2, word3, word4)
+ tc.formvalue('propertyform', 'field-newfield', val)
+ tc.submit('submit')
+ tc.find('<em>%s</em> <em>%s</em> added; <em>%s</em> removed'
+ % (word3, word4, word1))
+
+ tc.formvalue('propertyform', 'field-newfield', '')
+ tc.submit('submit')
+ tc.find('<em>%s</em> <em>%s</em> <em>%s</em> removed'
+ % (word2, word3, word4))
+
+ val = "%s %s,%s" % (word1, word2, word3)
+ tc.formvalue('propertyform', 'field-newfield', val)
+ tc.submit('submit')
+ tc.find('<em>%s</em> <em>%s</em> <em>%s</em> added'
+ % (word1, word2, word3))
+ query1 = 'status=!closed&newfield=~%s' % word1
+ query2 = 'status=!closed&newfield=~%s' % word2
+ query3 = 'status=!closed&newfield=~%s' % word3
+
+
+ path_prefix = urlsplit(self._tester.url).path
+ querylink1 = '<a href="%s/query\?%s">%s</a>' % (path_prefix,
+ query1, word1)
+ querylink2 = '<a href="%s/query\?%s">%s</a>' % (path_prefix,
+ query2, word2)
+ querylink3 = '<a href="%s/query\?%s">%s</a>' % (path_prefix,
+ query3, word3)
+ querylinks = '%s %s, %s' % (querylink1, querylink2, querylink3)
+
+ # [BLOODHOUND] Different markup to render field values
+ self._tester.find_ticket_field('newfield', querylinks)
+
+
+# Ensure that overridden code will be loaded
+def functionalSuite(suite=None):
+ if not suite:
+ import trac.tests.functional.testcases
+ suite = trac.tests.functional.testcases.functionalSuite()
+
+ suite.addTest(TestTickets())
+
+ # [BLOODHOUND] there's no such thing like ticket preview
+ #suite.addTest(TestTicketPreview())
+
+ suite.addTest(TestTicketNoSummary())
+ suite.addTest(TestTicketAltFormats())
+ suite.addTest(TestTicketCSVFormat())
+ suite.addTest(TestTicketTabFormat())
+ suite.addTest(TestTicketRSSFormat())
+
+ # [BLOODHOUND] TODO: Move to BloodhoundSearch plugin
+ # suite.addTest(TestTicketSearch())
+ # suite.addTest(TestNonTicketSearch())
+
+ suite.addTest(TestTicketHistory())
+ suite.addTest(TestTicketHistoryDiff())
+ suite.addTest(TestTicketQueryLinks())
+ suite.addTest(TestTicketQueryOrClause())
+ suite.addTest(TestTicketCustomFieldTextNoFormat())
+ suite.addTest(TestTicketCustomFieldTextWikiFormat())
+ suite.addTest(TestTicketCustomFieldTextAreaNoFormat())
+ suite.addTest(TestTicketCustomFieldTextAreaWikiFormat())
+ suite.addTest(TestTicketCustomFieldTextReferenceFormat())
+ suite.addTest(TestTicketCustomFieldTextListFormat())
+ suite.addTest(RegressionTestTicket10828())
+ suite.addTest(TestTimelineTicketDetails())
+ suite.addTest(TestAdminComponent())
+ suite.addTest(TestAdminComponentDuplicates())
+ suite.addTest(TestAdminComponentRemoval())
+ suite.addTest(TestAdminComponentNonRemoval())
+ suite.addTest(TestAdminComponentDefault())
+ suite.addTest(TestAdminComponentDetail())
+ suite.addTest(TestAdminMilestone())
+ suite.addTest(TestAdminMilestoneSpace())
+ suite.addTest(TestAdminMilestoneDuplicates())
+ suite.addTest(TestAdminMilestoneDetail())
+ suite.addTest(TestAdminMilestoneDue())
+ suite.addTest(TestAdminMilestoneDetailDue())
+ suite.addTest(TestAdminMilestoneCompleted())
+ suite.addTest(TestAdminMilestoneCompletedFuture())
+ suite.addTest(TestAdminMilestoneRemove())
+ suite.addTest(TestAdminMilestoneRemoveMulti())
+ suite.addTest(TestAdminMilestoneNonRemoval())
+ suite.addTest(TestAdminMilestoneDefault())
+ suite.addTest(TestAdminPriority())
+ suite.addTest(TestAdminPriorityModify())
+ suite.addTest(TestAdminPriorityRemove())
+ suite.addTest(TestAdminPriorityRemoveMulti())
+ suite.addTest(TestAdminPriorityNonRemoval())
+ suite.addTest(TestAdminPriorityDefault())
+ suite.addTest(TestAdminPriorityDetail())
+ suite.addTest(TestAdminPriorityRenumber())
+ suite.addTest(TestAdminPriorityRenumberDup())
+ suite.addTest(TestAdminResolution())
+ suite.addTest(TestAdminResolutionDuplicates())
+ suite.addTest(TestAdminSeverity())
+ suite.addTest(TestAdminSeverityDuplicates())
+ suite.addTest(TestAdminType())
+ suite.addTest(TestAdminTypeDuplicates())
+ suite.addTest(TestAdminVersion())
+ suite.addTest(TestAdminVersionDuplicates())
+ suite.addTest(TestAdminVersionDetail())
+ suite.addTest(TestAdminVersionDetailTime())
+ suite.addTest(TestAdminVersionDetailCancel())
+ suite.addTest(TestAdminVersionRemove())
+ suite.addTest(TestAdminVersionRemoveMulti())
+ suite.addTest(TestAdminVersionNonRemoval())
+ suite.addTest(TestAdminVersionDefault())
+ suite.addTest(TestNewReport())
+ suite.addTest(TestReportRealmDecoration())
+ suite.addTest(RegressionTestRev5665())
+ suite.addTest(RegressionTestRev5994())
+
+ suite.addTest(RegressionTestTicket4447())
+ suite.addTest(RegressionTestTicket4630a())
+ suite.addTest(RegressionTestTicket4630b())
+ suite.addTest(RegressionTestTicket5022())
+ suite.addTest(RegressionTestTicket5394a())
+ suite.addTest(RegressionTestTicket5394b())
+ suite.addTest(RegressionTestTicket5497prep())
+ suite.addTest(RegressionTestTicket5497a())
+ suite.addTest(RegressionTestTicket5497b())
+ suite.addTest(RegressionTestTicket5497c())
+ suite.addTest(RegressionTestTicket5497d())
+ suite.addTest(RegressionTestTicket5602())
+ suite.addTest(RegressionTestTicket5687())
+ suite.addTest(RegressionTestTicket5930())
+ suite.addTest(RegressionTestTicket6048())
+ suite.addTest(RegressionTestTicket6747())
+ suite.addTest(RegressionTestTicket6879a())
+ suite.addTest(RegressionTestTicket6879b())
+ suite.addTest(RegressionTestTicket6912a())
+ suite.addTest(RegressionTestTicket6912b())
+ suite.addTest(RegressionTestTicket7821group())
+ suite.addTest(RegressionTestTicket7821var())
+ suite.addTest(RegressionTestTicket8247())
+ suite.addTest(RegressionTestTicket8861())
+ suite.addTest(RegressionTestTicket9084())
+ suite.addTest(RegressionTestTicket9981())
+
+ return suite
+
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='functionalSuite')
Modified: bloodhound/trunk/trac/trac/tests/functional/__init__.py
URL:
http://svn.apache.org/viewvc/bloodhound/trunk/trac/trac/tests/functional/__init__.py?rev=1527447&r1=1527446&r2=1527447&view=diff
==============================================================================
--- bloodhound/trunk/trac/trac/tests/functional/__init__.py (original)
+++ bloodhound/trunk/trac/trac/tests/functional/__init__.py Mon Sep 30 05:31:52
2013
@@ -104,6 +104,8 @@ if twill:
else:
env_class = FunctionalTestEnvironment
+ tester_class = FunctionalTester
+
def setUp(self, port=None):
"""If no port is specified, use a semi-random port and subdirectory
'testenv'; but if a port is specified, use that port and
@@ -119,7 +121,7 @@ if twill:
baseurl = "http://127.0.0.1:%s" % port
self._testenv = self.env_class(dirname, port, baseurl)
self._testenv.start()
- self._tester = FunctionalTester(baseurl)
+ self._tester = self.tester_class(baseurl)
self.fixture = (self._testenv, self._tester)
def tearDown(self):