Merge lp:~cjwatson/python-pgbouncer/tox into lp:python-pgbouncer

Proposed by Colin Watson
Status: Merged
Merged at revision: 14
Proposed branch: lp:~cjwatson/python-pgbouncer/tox
Merge into: lp:python-pgbouncer
Diff against target: 538 lines (+54/-373)
9 files modified
.bzrignore (+2/-7)
NEWS.txt (+6/-0)
README (+13/-20)
bootstrap.py (+0/-259)
buildout.cfg (+0/-35)
pgbouncer/tests.py (+20/-34)
setup.py (+5/-3)
tox.ini (+8/-0)
versions.cfg (+0/-15)
To merge this branch: bzr merge lp:~cjwatson/python-pgbouncer/tox
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+368549@code.launchpad.net

Commit message

Use postgresfixture to test against multiple PostgreSQL versions, and switch to tox.

Description of the change

Regarding postgresfixture, I sacrificed the clever inter-test resource management as part of this work. It might be possible to make it work again, but the test suite isn't all that slow, and this at least seems pretty robust.

Regarding tox, I generally can't get buildout working these days, not least because of bad interactions with pbr, and so have been progressively converting packages to tox as I touch them. This also makes it easier to set up test matrixes that test against things like multiple Python versions.

These should really be two separate MPs, but I couldn't get tox working without doing something about the PostgreSQL test setup, and I couldn't get tests running at all without switching to tox, so they ended up a bit intertwined. Hopefully this isn't too hard to follow.

To post a comment you must log in.
Revision history for this message
William Grant (wgrant) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2011-10-25 20:03:51 +0000
3+++ .bzrignore 2019-06-07 15:00:45 +0000
4@@ -1,10 +1,5 @@
5-./eggs/*
6-./.installed.cfg
7-./develop-eggs
8-./bin
9+./.tox
10+./MANIFEST
11 ./pgbouncer.egg-info
12-./parts
13-./eggs
14-./download-cache
15 TAGS
16 tags
17
18=== modified file 'NEWS.txt'
19--- NEWS.txt 2012-08-20 16:39:41 +0000
20+++ NEWS.txt 2019-06-07 15:00:45 +0000
21@@ -1,3 +1,9 @@
22+0.0.9
23+=====
24+
25+- Use postgresfixture to test against multiple PostgreSQL versions.
26+- Switch to tox.
27+
28 0.0.8 (2012-08-20)
29 ==================
30
31
32=== modified file 'README'
33--- README 2011-10-25 20:03:29 +0000
34+++ README 2019-06-07 15:00:45 +0000
35@@ -28,22 +28,22 @@
36 * pgbouncer
37
38 * python-fixtures (https://launchpad.net/python-fixtures or
39- http://pypi.python.org/pypi/fixtures)
40+ https://pypi.org/project/fixtures)
41
42-* testtools (http://pypi.python.org/pypi/testtools)
43+* testtools (https://pypi.org/project/testtools)
44
45 Testing Dependencies
46 ====================
47
48 In addition to the above, the tests also depend on:
49
50-* psycopg2 (http://pypi.python.org/pypi/psycopg2)
51-
52-* subunit (http://pypi.python.org/pypi/python-subunit) (optional)
53-
54-* testresources (http://pypi.python.org/pypi/testresources)
55-
56-* van.pg (http://pypi.python.org/pypi/van.pg)
57+* postgresfixture (https://pypi.org/project/postgresfixture)
58+
59+* psycopg2 (https://pypi.org/project/psycopg2)
60+
61+* subunit (https://pypi.org/project/python-subunit) (optional)
62+
63+* testscenarios (https://pypi.org/project/testscenarios)
64
65 Usage
66 =====
67@@ -77,14 +77,7 @@
68 ===========
69
70 Upstream development takes place at https://launchpad.net/python-pgbouncer.
71-To setup a working area for development, if the dependencies are not
72-immediately available, you can use ./bootstrap.py to create bin/buildout, then
73-bin/py to get a python interpreter with the dependencies available.
74-
75-To run the tests, run either:
76-
77- $ bin/py pgbouncer/tests.py
78-
79-or:
80-
81- $ bin/py -m testtools.run pgbouncer.tests.test_suite
82+
83+To run the tests, run:
84+
85+ $ tox
86
87=== removed file 'bootstrap.py'
88--- bootstrap.py 2011-07-18 03:31:27 +0000
89+++ bootstrap.py 1970-01-01 00:00:00 +0000
90@@ -1,259 +0,0 @@
91-#!/usr/bin/env python
92-##############################################################################
93-#
94-# Copyright (c) 2006 Zope Foundation and Contributors.
95-# All Rights Reserved.
96-#
97-# This software is subject to the provisions of the Zope Public License,
98-# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
99-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
100-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
101-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
102-# FOR A PARTICULAR PURPOSE.
103-#
104-##############################################################################
105-"""Bootstrap a buildout-based project
106-
107-Simply run this script in a directory containing a buildout.cfg.
108-The script accepts buildout command-line options, so you can
109-use the -c option to specify an alternate configuration file.
110-"""
111-
112-import os, shutil, sys, tempfile, textwrap, urllib, urllib2, subprocess
113-from optparse import OptionParser
114-
115-if sys.platform == 'win32':
116- def quote(c):
117- if ' ' in c:
118- return '"%s"' % c # work around spawn lamosity on windows
119- else:
120- return c
121-else:
122- quote = str
123-
124-# See zc.buildout.easy_install._has_broken_dash_S for motivation and comments.
125-stdout, stderr = subprocess.Popen(
126- [sys.executable, '-Sc',
127- 'try:\n'
128- ' import ConfigParser\n'
129- 'except ImportError:\n'
130- ' print 1\n'
131- 'else:\n'
132- ' print 0\n'],
133- stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
134-has_broken_dash_S = bool(int(stdout.strip()))
135-
136-# In order to be more robust in the face of system Pythons, we want to
137-# run without site-packages loaded. This is somewhat tricky, in
138-# particular because Python 2.6's distutils imports site, so starting
139-# with the -S flag is not sufficient. However, we'll start with that:
140-if not has_broken_dash_S and 'site' in sys.modules:
141- # We will restart with python -S.
142- args = sys.argv[:]
143- args[0:0] = [sys.executable, '-S']
144- args = map(quote, args)
145- os.execv(sys.executable, args)
146-# Now we are running with -S. We'll get the clean sys.path, import site
147-# because distutils will do it later, and then reset the path and clean
148-# out any namespace packages from site-packages that might have been
149-# loaded by .pth files.
150-clean_path = sys.path[:]
151-import site
152-sys.path[:] = clean_path
153-for k, v in sys.modules.items():
154- if (hasattr(v, '__path__') and
155- len(v.__path__)==1 and
156- not os.path.exists(os.path.join(v.__path__[0],'__init__.py'))):
157- # This is a namespace package. Remove it.
158- sys.modules.pop(k)
159-
160-is_jython = sys.platform.startswith('java')
161-
162-setuptools_source = 'http://peak.telecommunity.com/dist/ez_setup.py'
163-distribute_source = 'http://python-distribute.org/distribute_setup.py'
164-
165-# parsing arguments
166-def normalize_to_url(option, opt_str, value, parser):
167- if value:
168- if '://' not in value: # It doesn't smell like a URL.
169- value = 'file://%s' % (
170- urllib.pathname2url(
171- os.path.abspath(os.path.expanduser(value))),)
172- if opt_str == '--download-base' and not value.endswith('/'):
173- # Download base needs a trailing slash to make the world happy.
174- value += '/'
175- else:
176- value = None
177- name = opt_str[2:].replace('-', '_')
178- setattr(parser.values, name, value)
179-
180-usage = '''\
181-[DESIRED PYTHON FOR BUILDOUT] bootstrap.py [options]
182-
183-Bootstraps a buildout-based project.
184-
185-Simply run this script in a directory containing a buildout.cfg, using the
186-Python that you want bin/buildout to use.
187-
188-Note that by using --setup-source and --download-base to point to
189-local resources, you can keep this script from going over the network.
190-'''
191-
192-parser = OptionParser(usage=usage)
193-parser.add_option("-v", "--version", dest="version",
194- help="use a specific zc.buildout version")
195-parser.add_option("-d", "--distribute",
196- action="store_true", dest="use_distribute", default=False,
197- help="Use Distribute rather than Setuptools.")
198-parser.add_option("--setup-source", action="callback", dest="setup_source",
199- callback=normalize_to_url, nargs=1, type="string",
200- help=("Specify a URL or file location for the setup file. "
201- "If you use Setuptools, this will default to " +
202- setuptools_source + "; if you use Distribute, this "
203- "will default to " + distribute_source +"."))
204-parser.add_option("--download-base", action="callback", dest="download_base",
205- callback=normalize_to_url, nargs=1, type="string",
206- help=("Specify a URL or directory for downloading "
207- "zc.buildout and either Setuptools or Distribute. "
208- "Defaults to PyPI."))
209-parser.add_option("--eggs",
210- help=("Specify a directory for storing eggs. Defaults to "
211- "a temporary directory that is deleted when the "
212- "bootstrap script completes."))
213-parser.add_option("-t", "--accept-buildout-test-releases",
214- dest='accept_buildout_test_releases',
215- action="store_true", default=False,
216- help=("Normally, if you do not specify a --version, the "
217- "bootstrap script and buildout gets the newest "
218- "*final* versions of zc.buildout and its recipes and "
219- "extensions for you. If you use this flag, "
220- "bootstrap and buildout will get the newest releases "
221- "even if they are alphas or betas."))
222-parser.add_option("-c", None, action="store", dest="config_file",
223- help=("Specify the path to the buildout configuration "
224- "file to be used."))
225-
226-options, args = parser.parse_args()
227-
228-# if -c was provided, we push it back into args for buildout's main function
229-if options.config_file is not None:
230- args += ['-c', options.config_file]
231-
232-if options.eggs:
233- eggs_dir = os.path.abspath(os.path.expanduser(options.eggs))
234-else:
235- eggs_dir = tempfile.mkdtemp()
236-
237-if options.setup_source is None:
238- if options.use_distribute:
239- options.setup_source = distribute_source
240- else:
241- options.setup_source = setuptools_source
242-
243-if options.accept_buildout_test_releases:
244- args.append('buildout:accept-buildout-test-releases=true')
245-args.append('bootstrap')
246-
247-try:
248- import pkg_resources
249- import setuptools # A flag. Sometimes pkg_resources is installed alone.
250- if not hasattr(pkg_resources, '_distribute'):
251- raise ImportError
252-except ImportError:
253- ez_code = urllib2.urlopen(
254- options.setup_source).read().replace('\r\n', '\n')
255- ez = {}
256- exec ez_code in ez
257- setup_args = dict(to_dir=eggs_dir, download_delay=0)
258- if options.download_base:
259- setup_args['download_base'] = options.download_base
260- if options.use_distribute:
261- setup_args['no_fake'] = True
262- ez['use_setuptools'](**setup_args)
263- reload(sys.modules['pkg_resources'])
264- import pkg_resources
265- # This does not (always?) update the default working set. We will
266- # do it.
267- for path in sys.path:
268- if path not in pkg_resources.working_set.entries:
269- pkg_resources.working_set.add_entry(path)
270-
271-cmd = [quote(sys.executable),
272- '-c',
273- quote('from setuptools.command.easy_install import main; main()'),
274- '-mqNxd',
275- quote(eggs_dir)]
276-
277-if not has_broken_dash_S:
278- cmd.insert(1, '-S')
279-
280-find_links = options.download_base
281-if not find_links:
282- find_links = os.environ.get('bootstrap-testing-find-links')
283-if find_links:
284- cmd.extend(['-f', quote(find_links)])
285-
286-if options.use_distribute:
287- setup_requirement = 'distribute'
288-else:
289- setup_requirement = 'setuptools'
290-ws = pkg_resources.working_set
291-setup_requirement_path = ws.find(
292- pkg_resources.Requirement.parse(setup_requirement)).location
293-env = dict(
294- os.environ,
295- PYTHONPATH=setup_requirement_path)
296-
297-requirement = 'zc.buildout'
298-version = options.version
299-if version is None and not options.accept_buildout_test_releases:
300- # Figure out the most recent final version of zc.buildout.
301- import setuptools.package_index
302- _final_parts = '*final-', '*final'
303- def _final_version(parsed_version):
304- for part in parsed_version:
305- if (part[:1] == '*') and (part not in _final_parts):
306- return False
307- return True
308- index = setuptools.package_index.PackageIndex(
309- search_path=[setup_requirement_path])
310- if find_links:
311- index.add_find_links((find_links,))
312- req = pkg_resources.Requirement.parse(requirement)
313- if index.obtain(req) is not None:
314- best = []
315- bestv = None
316- for dist in index[req.project_name]:
317- distv = dist.parsed_version
318- if _final_version(distv):
319- if bestv is None or distv > bestv:
320- best = [dist]
321- bestv = distv
322- elif distv == bestv:
323- best.append(dist)
324- if best:
325- best.sort()
326- version = best[-1].version
327-if version:
328- requirement = '=='.join((requirement, version))
329-cmd.append(requirement)
330-
331-if is_jython:
332- import subprocess
333- exitcode = subprocess.Popen(cmd, env=env).wait()
334-else: # Windows prefers this, apparently; otherwise we would prefer subprocess
335- exitcode = os.spawnle(*([os.P_WAIT, sys.executable] + cmd + [env]))
336-if exitcode != 0:
337- sys.stdout.flush()
338- sys.stderr.flush()
339- print ("An error occurred when trying to install zc.buildout. "
340- "Look above this message for any errors that "
341- "were output by easy_install.")
342- sys.exit(exitcode)
343-
344-ws.add_entry(eggs_dir)
345-ws.require(requirement)
346-import zc.buildout.buildout
347-zc.buildout.buildout.main(args)
348-if not options.eggs: # clean up temporary egg directory
349- shutil.rmtree(eggs_dir)
350
351=== removed file 'buildout.cfg'
352--- buildout.cfg 2012-08-20 16:27:22 +0000
353+++ buildout.cfg 1970-01-01 00:00:00 +0000
354@@ -1,35 +0,0 @@
355-# Copyright 2011 Canonical Ltd. This software is licensed under the
356-# GNU Affero General Public License version 3 (see the file LICENSE).
357-
358-[buildout]
359-parts =
360- scripts
361-unzip = true
362-eggs-directory = eggs
363-download-cache = download-cache
364-relative-paths = true
365-
366-# Disable this option temporarily if you want buildout to find software
367-# dependencies *other* than those in our download-cache. Once you have the
368-# desired software, reenable this option (and check in the new software to
369-# lp:lp-source-dependencies if this is going to be reviewed/merged/deployed.)
370-install-from-cache = true
371-
372-# This also will need to be temporarily disabled or changed for package
373-# upgrades. Newly-added packages should also add their desired version number
374-# to versions.cfg.
375-extends = versions.cfg
376-
377-allow-picked-versions = false
378-
379-prefer-final = true
380-
381-develop = .
382-
383-[scripts]
384-recipe = z3c.recipe.scripts
385-eggs = pgbouncer [test]
386-include-site-packages = true
387-allowed-eggs-from-site-packages =
388- subunit
389-interpreter = py
390
391=== modified file 'pgbouncer/tests.py'
392--- pgbouncer/tests.py 2011-10-28 11:45:32 +0000
393+++ pgbouncer/tests.py 2019-06-07 15:00:45 +0000
394@@ -14,51 +14,42 @@
395 # You should have received a copy of the GNU Affero General Public License
396 # along with this program. If not, see <http://www.gnu.org/licenses/>.
397
398-import os
399-import unittest
400+from contextlib import closing
401
402 import fixtures
403+from postgresfixture import ClusterFixture
404+from postgresfixture.cluster import PG_VERSIONS
405 import psycopg2
406-from van.pg import DatabaseManager
407-import testresources
408+import testscenarios
409 import testtools
410
411 from pgbouncer.fixture import PGBouncerFixture
412
413-# A 'just-works' workaround for Ubuntu not exposing initdb to the main PATH.
414-os.environ['PATH'] = os.environ['PATH'] + ':/usr/lib/postgresql/8.4/bin'
415-
416-
417-def test_suite():
418- loader = testresources.TestLoader()
419- return loader.loadTestsFromName(__name__)
420-
421-
422-class ResourcedTestCase(testtools.TestCase, testresources.ResourcedTestCase):
423- """Mix together testtools and testresources."""
424-
425-
426-def setup_user(db):
427- conn = psycopg2.connect(host=db.host, database=db.database)
428- conn.cursor().execute('CREATE USER user1')
429- conn.commit()
430- conn.close()
431-
432-
433-class TestFixture(ResourcedTestCase):
434-
435- resources = [('db', DatabaseManager(initialize_sql=setup_user))]
436+
437+class TestFixture(testscenarios.WithScenarios, testtools.TestCase):
438+
439+ scenarios = sorted(
440+ (version, {'version': version}) for version in PG_VERSIONS)
441
442 def setUp(self):
443 super(TestFixture, self).setUp()
444+ datadir = self.useFixture(fixtures.TempDir()).path
445+ self.dbname = 'test_pgbouncer'
446+ self.cluster = self.useFixture(
447+ ClusterFixture(datadir, version=self.version))
448+ self.cluster.createdb(self.dbname)
449+ with closing(self.cluster.connect()) as conn:
450+ with closing(conn.cursor()) as cur:
451+ cur.execute('DROP USER IF EXISTS user1')
452+ cur.execute('CREATE USER user1')
453 self.bouncer = PGBouncerFixture()
454- self.bouncer.databases[self.db.database] = 'host=' + self.db.host
455+ self.bouncer.databases[self.dbname] = 'host=' + datadir
456 self.bouncer.users['user1'] = ''
457
458 def connect(self, host=None):
459 return psycopg2.connect(
460 host=(self.bouncer.host if host is None else host),
461- port=self.bouncer.port, database=self.db.database,
462+ port=self.bouncer.port, database=self.dbname,
463 user='user1')
464
465 def test_dynamic_port_allocation(self):
466@@ -99,8 +90,3 @@
467 bouncer_pid = self.bouncer.process.pid
468 self.bouncer.start()
469 self.assertEqual(bouncer_pid, self.bouncer.process.pid)
470-
471-
472-if __name__ == "__main__":
473- loader = testresources.TestLoader()
474- unittest.main(testLoader=loader)
475
476=== modified file 'setup.py'
477--- setup.py 2012-08-20 16:39:41 +0000
478+++ setup.py 2019-06-07 15:00:45 +0000
479@@ -14,12 +14,13 @@
480 #
481 # You should have received a copy of the GNU Affero General Public License
482 # along with this program. If not, see <http://www.gnu.org/licenses/>.
483-
484+
485
486 from distutils.core import setup
487 import os.path
488
489-description = file(os.path.join(os.path.dirname(__file__), 'README'), 'rb').read()
490+with open(os.path.join(os.path.dirname(__file__), 'README')) as f:
491+ description = f.read()
492
493 setup(name="pgbouncer",
494 version="0.0.8",
495@@ -43,8 +44,9 @@
496 ],
497 extras_require = dict(
498 test=[
499+ 'postgresfixture',
500+ 'testscenarios',
501 'testtools',
502- 'van.pg',
503 ]
504 ),
505 )
506
507=== added file 'tox.ini'
508--- tox.ini 1970-01-01 00:00:00 +0000
509+++ tox.ini 2019-06-07 15:00:45 +0000
510@@ -0,0 +1,8 @@
511+[tox]
512+envlist =
513+ py27
514+
515+[testenv]
516+deps =
517+ .[test]
518+commands = python -m testtools.run discover -s pgbouncer {posargs}
519
520=== removed file 'versions.cfg'
521--- versions.cfg 2012-08-20 16:27:22 +0000
522+++ versions.cfg 1970-01-01 00:00:00 +0000
523@@ -1,15 +0,0 @@
524-[buildout]
525-versions = versions
526-
527-[versions]
528-fixtures = 0.3.6
529-psycopg2 = 2.4.5
530-pytz = 2012d
531-setuptools = 0.6c11
532-testresources = 0.2.4
533-testtools = 0.9.11
534-van.pg = 1.4
535-z3c.recipe.filetemplate = 2.1.0
536-z3c.recipe.scripts = 1.0.1
537-zc.buildout = 1.5.1
538-zc.recipe.egg = 1.3.2

Subscribers

People subscribed via source and target branches

to status/vote changes: