-
Notifications
You must be signed in to change notification settings - Fork 22
Expand file tree
/
Copy pathtest_quickstart.py
More file actions
401 lines (312 loc) · 13.7 KB
/
Copy pathtest_quickstart.py
File metadata and controls
401 lines (312 loc) · 13.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
import os
import shutil
import subprocess
import sys
import site
import unittest
from webtest import TestApp
from itertools import count
from venv import EnvBuilder
from tg.util import Bunch
from devtools.gearbox.quickstart import QuickstartCommand
from gearbox.commands.setup_app import SetupAppCommand
PY_VERSION = sys.version_info[:2]
PROJECT_NAME = 'TGTest-%02d'
ENV_NAME = 'TESTENV'
CLEANUP = True
COUNTER = count()
QUIET = '-q' # Set this to -v to enable installed packages logging, or to -q to disable it
def get_passed_and_failed(env_cmd, python_cmd, testpath):
"""Run test suite under testpath, return set of passed tests."""
os.chdir(testpath)
args = '. %s; %s -W ignore -mpytest -v --no-header --no-summary' % (env_cmd, python_cmd)
out = subprocess.Popen(args,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
shell=True, encoding="utf-8").communicate()[0]
passed, failed = [], []
lines = out.splitlines()
for line in lines:
test = None
line = line.split(' ', 1)
if line[0].startswith('tgtest'):
test = line[0]
if test and len(line) == 2:
line[1] = line[1].split()[0].strip()
if line[1] in ('passed', 'PASSED'):
passed.append(test)
test = None
elif line[1] in ('ERROR', 'FAILED'):
failed.append(test)
test = None
return passed, failed, lines
class BaseTestQuickStart(object):
args = ''
preinstall = []
@classmethod
def setUpClass(cls):
cls.command = QuickstartCommand(None, {})
cls.parser = cls.command.get_parser('tg2devtools-test')
cls.base_dir = os.getcwd()
# All the envs must be named equally due to python not supporting
# unloading modules, so the modules loaded on first fixture must
# be in the same place on the next fixtures.
cls.env_dir = os.path.join(os.path.abspath(cls.base_dir), ENV_NAME)
# directory for executable scripts in the virtual environment
cls.bin_dir = os.path.join(cls.env_dir,
'Scripts' if sys.platform == 'win32' else 'bin')
# This is to avoid previously failed tests to break successive fixtures
shutil.rmtree(cls.env_dir, ignore_errors=True)
# Create virtualenv for current fixture
EnvBuilder(with_pip=True, symlinks=True).create(cls.env_dir)
# Enable the newly created virtualenv
cls.pip_cmd, cls.python_cmd, cls.env_cmd, site_packages = cls.enter_virtualenv()
# Reinstall gearbox to force it being installed inside the
# virtualenv using supported PBR version
cls.run_pip(['install', '-U', 'setuptools', 'pip', 'wheel'])
cls.run_pip(['install', '-I', 'git+https://github.com/TurboGears/tempita'])
cls.run_pip(['install', '--pre', '-I', 'gearbox'])
# Then install specific requirements
for p in cls.preinstall:
cls.run_pip(['install', '--pre', '-I', p])
cls.run_pip(['install', '-I', 'git+https://github.com/TurboGears/crank'])
cls.run_pip(['install', '-I', 'git+https://github.com/TurboGears/backlash'])
cls.run_pip(['install', '-I', 'git+https://github.com/TurboGears/tgext.debugbar'])
# Install TurboGears from development branch to test future compatibility
cls.venv_uninstall('WebOb')
cls.venv_uninstall('TurboGears2')
cls.run_pip(['install', '--pre', '-I', 'git+https://github.com/TurboGears/tg2.git@development'])
# Install tg.devtools inside the virtualenv
cls.run_pip(['install', '--pre', '-e', cls.base_dir])
# Install All Template Engines inside the virtualenv so that
# They all get configured as we share a single python process
# for all configurations.
for engine in ("Jinja2", "Genshi", "make", "kajiki"):
cls.run_pip(['install', '--upgrade', '--no-deps', '--force-reinstall',
'--pre', engine])
# This is to avoid the TGTest package to be detected as
# being already installed.
proj_name = PROJECT_NAME % next(COUNTER)
cls.proj_dir = os.path.join(cls.base_dir, proj_name)
# Create a quickstarted app by runnig 'gearbox quickstart'
opts = cls.parser.parse_args(cls.args.split() + [proj_name])
cls.command.run(opts)
# Install quickstarted project dependencies
cls.run_pip(['install', '--pre', '-e', '%s[testing]' % cls.proj_dir])
# Mark the packages as installed even outside the virtualenv
# so we can load app in tests which are not executed inside
# the newly created virtualenv.
site.addsitedir(site_packages)
def setUp(self):
os.chdir(self.proj_dir)
from paste.deploy import loadapp
self.app = loadapp('config:test.ini', relative_to=self.proj_dir)
self.app = TestApp(self.app)
def init_database(self):
os.chdir(self.proj_dir)
cmd = SetupAppCommand(Bunch(options=Bunch(verbose_level=1)), Bunch())
try:
cmd.run(Bunch(config_file='config:test.ini', section_name=None))
except:
# DB already initialised, ignore it.
pass
@classmethod
def tearDownClass(cls):
# This is in case the tests have been skipped
if not hasattr(cls, 'past_working_set_state'):
return
cls.exit_virtualenv()
os.chdir(cls.base_dir)
if CLEANUP:
shutil.rmtree(cls.proj_dir, ignore_errors=False)
shutil.rmtree(cls.env_dir, ignore_errors=False)
@classmethod
def enter_virtualenv(cls):
cls.old_os_path = os.environ['PATH']
os.environ['PATH'] = cls.env_dir + os.pathsep + cls.old_os_path
base = os.path.abspath(cls.env_dir)
site_packages = os.path.join(
base, 'lib', 'python%s.%s' % sys.version_info[:2], 'site-packages'
)
cls.prev_sys_path = list(sys.path)
cls.past_prefix = sys.prefix
cls.past_real_prefix = getattr(sys, 'real_prefix', None)
site.addsitedir(site_packages)
sys.real_prefix = sys.prefix
sys.prefix = base
# Move the added items to the front of the path:
new_sys_path = []
for item in list(sys.path):
if item not in cls.prev_sys_path:
new_sys_path.append(item)
sys.path.remove(item)
sys.path[:0] = new_sys_path
return (os.path.join(cls.bin_dir, 'pip'),
os.path.join(cls.bin_dir, 'python'),
os.path.join(cls.bin_dir, 'activate'),
site_packages)
@classmethod
def exit_virtualenv(cls):
os.environ['PATH'] = cls.old_os_path
sys.path = cls.prev_sys_path
sys.prefix = cls.past_prefix
if cls.past_real_prefix is not None:
sys.real_prefix = cls.past_real_prefix
else:
delattr(sys, 'real_prefix')
@classmethod
def run_pip(cls, opt):
return subprocess.call([cls.python_cmd, '-mpip', QUIET] + opt)
@classmethod
def venv_uninstall(cls, package):
# Do it 5 times to ensure it was uninstalled for real.
# Due to the -I used in other commands,
# multiple versions of the same package might be installed concurrently.
for i in range(5):
cls.run_pip(['uninstall', '-y', package])
class CommonTestQuickStart(BaseTestQuickStart):
# tests that must be passed
pass_tests = [
'/tests/functional/test_authentication.',
'/tests/functional/test_root.',
'/tests/models/test_auth.']
# tests that must fail (should not exist)
fail_tests = []
# tests that must not be run
skip_tests = []
def test_index(self):
resp = self.app.get('/')
assert 'Welcome to TurboGears' in resp, resp
def test_login(self):
resp = self.app.get('/login')
assert '<h1>Login</h1>' in resp
def test_subtests(self):
passed, failed, lines = get_passed_and_failed(self.env_cmd,
self.python_cmd,
os.path.join(self.proj_dir))
for has_failed in failed:
for must_fail in self.fail_tests:
if must_fail in has_failed:
break
else:
assert False, 'Failed %s\n\n%s' % (has_failed, '\n'.join(lines))
for must_pass in self.pass_tests:
for has_passed in passed:
if must_pass in has_passed:
break
else:
print("Passed:\n" + '\n'.join(passed))
assert False, 'Did not pass %s\n\n%s' % (must_pass, '\n'.join(lines))
for must_fail in self.fail_tests:
for has_failed in failed:
if must_fail in has_failed:
break
else:
print("Failed:\n" + '\n'.join(failed))
assert False, 'Did not fail %s' % must_fail
for must_skip in self.skip_tests:
for has_run in passed + failed:
if must_skip in has_run:
print("Run:\n" + '\n'.join(passed + failed))
assert False, 'Did not skip %s' % must_skip
class CommonTestQuickStartWithAuth(CommonTestQuickStart):
def test_secured_controller(self):
assert (
'<h1>Login</h1>' in self.app.get('/secc/', status=302).follow()
)
def test_secured_controller_with_prefix(self):
resp1 = self.app.get('/prefix/secc/', extra_environ={'SCRIPT_NAME': '/prefix'}, status=302)
assert (
resp1.headers['Location'] == 'http://localhost/prefix/login?came_from=%2Fprefix%2Fsecc%2F'
), resp1.headers['Location']
resp2 = resp1.follow(extra_environ={'SCRIPT_NAME': '/prefix'})
assert '/prefix/login_handler' in resp2, resp2
def test_login_with_prefix(self):
self.init_database()
resp1 = self.app.post('/prefix/login_handler?came_from=%2Fprefix%2Fsecc%2F',
params={'login': 'editor', 'password': 'editpass'},
extra_environ={'SCRIPT_NAME': '/prefix'})
assert (
resp1.headers['Location'] == 'http://localhost/prefix/post_login?came_from=%2Fprefix%2Fsecc%2F'
), resp1.headers['Location']
resp2 = resp1.follow(extra_environ={'SCRIPT_NAME': '/prefix'})
assert (
resp2.headers['Location'] == 'http://localhost/prefix/secc/'
), resp2.headers['Location']
def test_login_failure_with_prefix(self):
self.init_database()
resp = self.app.post('/prefix/login_handler?came_from=%2Fprefix%2Fsecc%2F',
params={'login': 'WRONG', 'password': 'WRONG'},
extra_environ={'SCRIPT_NAME': '/prefix'})
location = resp.headers['Location']
assert 'http://localhost/prefix/login' in location, location
assert 'came_from=%2Fprefix%2Fsecc%2F' in location, location
class TestDefaultQuickStart(CommonTestQuickStartWithAuth, unittest.TestCase):
args = ''
@classmethod
def setUpClass(cls):
super(TestDefaultQuickStart, cls).setUpClass()
def setUp(self):
super(TestDefaultQuickStart, self).setUp()
class TestMakoQuickStart(CommonTestQuickStart, unittest.TestCase):
args = '--mako --nosa --noauth'
pass_tests = ['/tests/functional/test_root.']
skip_tests = [
'TestRootController::test_secc',
'/tests/functional/test_authentication.',
'/tests/models/test_auth.']
def test_login(self):
self.app.get('/login', status=404)
class TestGenshiQuickStart(CommonTestQuickStart, unittest.TestCase):
args = '--genshi --nosa --noauth'
pass_tests = ['/tests/functional/test_root.']
skip_tests = [
'TestRootController::test_secc',
'/tests/functional/test_authentication.',
'/tests/models/test_auth.']
def test_login(self):
self.app.get('/login', status=404)
class TestJinjaQuickStart(CommonTestQuickStart, unittest.TestCase):
args = '--jinja --nosa --noauth'
pass_tests = ['/tests/functional/test_root.']
skip_tests = [
'TestRootController::test_secc',
'/tests/functional/test_authentication.',
'/tests/models/test_auth.']
def test_login(self):
self.app.get('/login', status=404)
class TestNoDBQuickStart(CommonTestQuickStart, unittest.TestCase):
pass_tests = ['/tests/functional/test_root.']
skip_tests = [
'TestRootController::test_secc',
'/tests/functional/test_authentication.',
'/tests/models/test_auth.']
args = '--nosa --noauth'
def test_login(self):
self.app.get('/login', status=404)
class TestNoAuthQuickStart(CommonTestQuickStart, unittest.TestCase):
pass_tests = ['/tests/functional/test_root.']
skip_tests = [
'TestRootController::test_secc',
'/tests/functional/test_authentication.',
'/tests/models/test_auth.']
args = '--noauth'
@classmethod
def setUpClass(cls):
super(TestNoAuthQuickStart, cls).setUpClass()
def setUp(self):
super(TestNoAuthQuickStart, self).setUp()
def test_login(self):
self.app.get('/login', status=404)
class TestMingBQuickStart(CommonTestQuickStartWithAuth, unittest.TestCase):
args = '--ming'
# preinstall = ['Paste', 'PasteScript'] # Ming doesn't require those anymore
@classmethod
def setUpClass(cls):
super(TestMingBQuickStart, cls).setUpClass()
def setUp(self):
super(TestMingBQuickStart, self).setUp()
class TestMinimalQuickStart(CommonTestQuickStart, unittest.TestCase):
args = '--minimal-quickstart'
def test_secc_is_removed(self):
self.app.get('/secc', status=404)