##// END OF EJS Templates
tests: pass in config ini into global configuration, same as the real app does.
marcink -
r2354:164d8f96 default
parent child Browse files
Show More
@@ -1,386 +1,386 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import os
22 22 import json
23 23 import platform
24 24 import socket
25 25 import subprocess32
26 26 import time
27 27 from urllib2 import urlopen, URLError
28 28
29 29 import configobj
30 30 import pytest
31 31
32 32 import pyramid.paster
33 33 from rhodecode.tests.fixture import TestINI
34 34 import rhodecode
35 35
36 36
37 37 def _parse_json(value):
38 38 return json.loads(value) if value else None
39 39
40 40
41 41 def pytest_addoption(parser):
42 42 parser.addoption(
43 43 '--test-loglevel', dest='test_loglevel',
44 44 help="Set default Logging level for tests, warn (default), info, debug")
45 45 group = parser.getgroup('pylons')
46 46 group.addoption(
47 47 '--with-pylons', dest='pylons_config',
48 48 help="Set up a Pylons environment with the specified config file.")
49 49 group.addoption(
50 50 '--ini-config-override', action='store', type=_parse_json,
51 51 default=None, dest='pylons_config_override', help=(
52 52 "Overrides the .ini file settings. Should be specified in JSON"
53 53 " format, e.g. '{\"section\": {\"parameter\": \"value\", ...}}'"
54 54 )
55 55 )
56 56 parser.addini(
57 57 'pylons_config',
58 58 "Set up a Pylons environment with the specified config file.")
59 59
60 60 vcsgroup = parser.getgroup('vcs')
61 61 vcsgroup.addoption(
62 62 '--without-vcsserver', dest='with_vcsserver', action='store_false',
63 63 help="Do not start the VCSServer in a background process.")
64 64 vcsgroup.addoption(
65 65 '--with-vcsserver-http', dest='vcsserver_config_http',
66 66 help="Start the HTTP VCSServer with the specified config file.")
67 67 vcsgroup.addoption(
68 68 '--vcsserver-protocol', dest='vcsserver_protocol',
69 69 help="Start the VCSServer with HTTP protocol support.")
70 70 vcsgroup.addoption(
71 71 '--vcsserver-config-override', action='store', type=_parse_json,
72 72 default=None, dest='vcsserver_config_override', help=(
73 73 "Overrides the .ini file settings for the VCSServer. "
74 74 "Should be specified in JSON "
75 75 "format, e.g. '{\"section\": {\"parameter\": \"value\", ...}}'"
76 76 )
77 77 )
78 78 vcsgroup.addoption(
79 79 '--vcsserver-port', action='store', type=int,
80 80 default=None, help=(
81 81 "Allows to set the port of the vcsserver. Useful when testing "
82 82 "against an already running server and random ports cause "
83 83 "trouble."))
84 84 parser.addini(
85 85 'vcsserver_config_http',
86 86 "Start the HTTP VCSServer with the specified config file.")
87 87 parser.addini(
88 88 'vcsserver_protocol',
89 89 "Start the VCSServer with HTTP protocol support.")
90 90
91 91
92 92 @pytest.fixture(scope='session')
93 93 def vcsserver(request, vcsserver_port, vcsserver_factory):
94 94 """
95 95 Session scope VCSServer.
96 96
97 97 Tests wich need the VCSServer have to rely on this fixture in order
98 98 to ensure it will be running.
99 99
100 100 For specific needs, the fixture vcsserver_factory can be used. It allows to
101 101 adjust the configuration file for the test run.
102 102
103 103 Command line args:
104 104
105 105 --without-vcsserver: Allows to switch this fixture off. You have to
106 106 manually start the server.
107 107
108 108 --vcsserver-port: Will expect the VCSServer to listen on this port.
109 109 """
110 110
111 111 if not request.config.getoption('with_vcsserver'):
112 112 return None
113 113
114 114 use_http = _use_vcs_http_server(request.config)
115 115 return vcsserver_factory(
116 116 request, use_http=use_http, vcsserver_port=vcsserver_port)
117 117
118 118
119 119 @pytest.fixture(scope='session')
120 120 def vcsserver_factory(tmpdir_factory):
121 121 """
122 122 Use this if you need a running vcsserver with a special configuration.
123 123 """
124 124
125 125 def factory(request, use_http=True, overrides=(), vcsserver_port=None):
126 126
127 127 if vcsserver_port is None:
128 128 vcsserver_port = get_available_port()
129 129
130 130 overrides = list(overrides)
131 131 if use_http:
132 132 overrides.append({'server:main': {'port': vcsserver_port}})
133 133 else:
134 134 overrides.append({'DEFAULT': {'port': vcsserver_port}})
135 135
136 136 if is_cygwin():
137 137 platform_override = {'DEFAULT': {
138 138 'beaker.cache.repo_object.type': 'nocache'}}
139 139 overrides.append(platform_override)
140 140
141 141 option_name = 'vcsserver_config_http' if use_http else ''
142 142 override_option_name = 'vcsserver_config_override'
143 143 config_file = get_config(
144 144 request.config, option_name=option_name,
145 145 override_option_name=override_option_name, overrides=overrides,
146 146 basetemp=tmpdir_factory.getbasetemp().strpath,
147 147 prefix='test_vcs_')
148 148
149 149 print("Using the VCSServer configuration:{}".format(config_file))
150 150 ServerClass = HttpVCSServer if use_http else None
151 151 server = ServerClass(config_file)
152 152 server.start()
153 153
154 154 @request.addfinalizer
155 155 def cleanup():
156 156 server.shutdown()
157 157
158 158 server.wait_until_ready()
159 159 return server
160 160
161 161 return factory
162 162
163 163
164 164 def is_cygwin():
165 165 return 'cygwin' in platform.system().lower()
166 166
167 167
168 168 def _use_vcs_http_server(config):
169 169 protocol_option = 'vcsserver_protocol'
170 170 protocol = (
171 171 config.getoption(protocol_option) or
172 172 config.getini(protocol_option) or
173 173 'http')
174 174 return protocol == 'http'
175 175
176 176
177 177 def _use_log_level(config):
178 178 level = config.getoption('test_loglevel') or 'warn'
179 179 return level.upper()
180 180
181 181
182 182 class VCSServer(object):
183 183 """
184 184 Represents a running VCSServer instance.
185 185 """
186 186
187 187 _args = []
188 188
189 189 def start(self):
190 190 print("Starting the VCSServer: {}".format(self._args))
191 191 self.process = subprocess32.Popen(self._args)
192 192
193 193 def wait_until_ready(self, timeout=30):
194 194 raise NotImplementedError()
195 195
196 196 def shutdown(self):
197 197 self.process.kill()
198 198
199 199
200 200 class HttpVCSServer(VCSServer):
201 201 """
202 202 Represents a running VCSServer instance.
203 203 """
204 204 def __init__(self, config_file):
205 205 config_data = configobj.ConfigObj(config_file)
206 206 self._config = config_data['server:main']
207 207
208 208 args = ['pserve', config_file]
209 209 self._args = args
210 210
211 211 @property
212 212 def http_url(self):
213 213 template = 'http://{host}:{port}/'
214 214 return template.format(**self._config)
215 215
216 216 def start(self):
217 217 self.process = subprocess32.Popen(self._args)
218 218
219 219 def wait_until_ready(self, timeout=30):
220 220 host = self._config['host']
221 221 port = self._config['port']
222 222 status_url = 'http://{host}:{port}/status'.format(host=host, port=port)
223 223 start = time.time()
224 224
225 225 while time.time() - start < timeout:
226 226 try:
227 227 urlopen(status_url)
228 228 break
229 229 except URLError:
230 230 time.sleep(0.2)
231 231 else:
232 232 pytest.exit(
233 233 "Starting the VCSServer failed or took more than {} "
234 234 "seconds. cmd: `{}`".format(timeout, ' '.join(self._args)))
235 235
236 236 def shutdown(self):
237 237 self.process.kill()
238 238
239 239
240 240 @pytest.fixture(scope='session')
241 241 def ini_config(request, tmpdir_factory, rcserver_port, vcsserver_port):
242 242 option_name = 'pylons_config'
243 243 log_level = _use_log_level(request.config)
244 244
245 245 overrides = [
246 246 {'server:main': {'port': rcserver_port}},
247 247 {'app:main': {
248 248 'vcs.server': 'localhost:%s' % vcsserver_port,
249 249 # johbo: We will always start the VCSServer on our own based on the
250 250 # fixtures of the test cases. For the test run it must always be
251 251 # off in the INI file.
252 252 'vcs.start_server': 'false',
253 253 }},
254 254
255 255 {'handler_console': {
256 256 'class ': 'StreamHandler',
257 257 'args ': '(sys.stderr,)',
258 258 'level': log_level,
259 259 }},
260 260
261 261 ]
262 262 if _use_vcs_http_server(request.config):
263 263 overrides.append({
264 264 'app:main': {
265 265 'vcs.server.protocol': 'http',
266 266 'vcs.scm_app_implementation': 'http',
267 267 'vcs.hooks.protocol': 'http',
268 268 }
269 269 })
270 270
271 271 filename = get_config(
272 272 request.config, option_name=option_name,
273 273 override_option_name='{}_override'.format(option_name),
274 274 overrides=overrides,
275 275 basetemp=tmpdir_factory.getbasetemp().strpath,
276 276 prefix='test_rce_')
277 277 return filename
278 278
279 279
280 280 @pytest.fixture(scope='session')
281 281 def rcserver_port(request):
282 282 port = get_available_port()
283 283 print('Using rcserver port {}'.format(port))
284 284 return port
285 285
286 286
287 287 @pytest.fixture(scope='session')
288 288 def vcsserver_port(request):
289 289 port = request.config.getoption('--vcsserver-port')
290 290 if port is None:
291 291 port = get_available_port()
292 292 print('Using vcsserver port {}'.format(port))
293 293 return port
294 294
295 295
296 296 def get_available_port():
297 297 family = socket.AF_INET
298 298 socktype = socket.SOCK_STREAM
299 299 host = '127.0.0.1'
300 300
301 301 mysocket = socket.socket(family, socktype)
302 302 mysocket.bind((host, 0))
303 303 port = mysocket.getsockname()[1]
304 304 mysocket.close()
305 305 del mysocket
306 306 return port
307 307
308 308
309 309 @pytest.fixture(scope='session')
310 310 def available_port_factory():
311 311 """
312 312 Returns a callable which returns free port numbers.
313 313 """
314 314 return get_available_port
315 315
316 316
317 317 @pytest.fixture
318 318 def available_port(available_port_factory):
319 319 """
320 320 Gives you one free port for the current test.
321 321
322 322 Uses "available_port_factory" to retrieve the port.
323 323 """
324 324 return available_port_factory()
325 325
326 326
327 327 @pytest.fixture(scope='session')
328 328 def baseapp(ini_config, vcsserver, http_environ_session):
329 329 from rhodecode.lib.pyramid_utils import get_app_config
330 330 from rhodecode.config.middleware import make_pyramid_app
331 331
332 332 print("Using the RhodeCode configuration:{}".format(ini_config))
333 333 pyramid.paster.setup_logging(ini_config)
334 334
335 335 settings = get_app_config(ini_config)
336 app = make_pyramid_app({}, **settings)
336 app = make_pyramid_app({'__file__': ini_config}, **settings)
337 337
338 338 return app
339 339
340 340
341 341 @pytest.fixture(scope='session')
342 342 def testini_factory(tmpdir_factory, ini_config):
343 343 """
344 344 Factory to create an INI file based on TestINI.
345 345
346 346 It will make sure to place the INI file in the correct directory.
347 347 """
348 348 basetemp = tmpdir_factory.getbasetemp().strpath
349 349 return TestIniFactory(basetemp, ini_config)
350 350
351 351
352 352 class TestIniFactory(object):
353 353
354 354 def __init__(self, basetemp, template_ini):
355 355 self._basetemp = basetemp
356 356 self._template_ini = template_ini
357 357
358 358 def __call__(self, ini_params, new_file_prefix='test'):
359 359 ini_file = TestINI(
360 360 self._template_ini, ini_params=ini_params,
361 361 new_file_prefix=new_file_prefix, dir=self._basetemp)
362 362 result = ini_file.create()
363 363 return result
364 364
365 365
366 366 def get_config(
367 367 config, option_name, override_option_name, overrides=None,
368 368 basetemp=None, prefix='test'):
369 369 """
370 370 Find a configuration file and apply overrides for the given `prefix`.
371 371 """
372 372 config_file = (
373 373 config.getoption(option_name) or config.getini(option_name))
374 374 if not config_file:
375 375 pytest.exit(
376 376 "Configuration error, could not extract {}.".format(option_name))
377 377
378 378 overrides = overrides or []
379 379 config_override = config.getoption(override_option_name)
380 380 if config_override:
381 381 overrides.append(config_override)
382 382 temp_ini_file = TestINI(
383 383 config_file, ini_params=overrides, new_file_prefix=prefix,
384 384 dir=basetemp)
385 385
386 386 return temp_ini_file.create()
General Comments 0
You need to be logged in to leave comments. Login now