##// END OF EJS Templates
pytest: renamed pylons_config into pyramid_config.
marcink -
r2369:b9736a2a default
parent child Browse files
Show More
@@ -1,15 +1,15 b''
1 [pytest]
1 [pytest]
2 testpaths = rhodecode
2 testpaths = rhodecode
3 norecursedirs = rhodecode/public rhodecode/templates tests/scripts
3 norecursedirs = rhodecode/public rhodecode/templates tests/scripts
4
4
5 pylons_config = rhodecode/tests/rhodecode.ini
5 pyramid_config = rhodecode/tests/rhodecode.ini
6 vcsserver_protocol = http
6 vcsserver_protocol = http
7 vcsserver_config_http = rhodecode/tests/vcsserver_http.ini
7 vcsserver_config_http = rhodecode/tests/vcsserver_http.ini
8
8
9 addopts =
9 addopts =
10 --pdbcls=IPython.terminal.debugger:TerminalPdb
10 --pdbcls=IPython.terminal.debugger:TerminalPdb
11
11
12 markers =
12 markers =
13 vcs_operations: Mark tests depending on a running RhodeCode instance.
13 vcs_operations: Mark tests depending on a running RhodeCode instance.
14 xfail_backends: Mark tests as xfail for given backends.
14 xfail_backends: Mark tests as xfail for given backends.
15 skip_backends: Mark tests as skipped for given backends.
15 skip_backends: Mark tests as skipped for given backends.
@@ -1,264 +1,264 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 py.test config for test suite for making push/pull operations.
22 py.test config for test suite for making push/pull operations.
23
23
24 .. important::
24 .. important::
25
25
26 You must have git >= 1.8.5 for tests to work fine. With 68b939b git started
26 You must have git >= 1.8.5 for tests to work fine. With 68b939b git started
27 to redirect things to stderr instead of stdout.
27 to redirect things to stderr instead of stdout.
28 """
28 """
29
29
30 import ConfigParser
30 import ConfigParser
31 import os
31 import os
32 import subprocess32
32 import subprocess32
33 import tempfile
33 import tempfile
34 import textwrap
34 import textwrap
35 import pytest
35 import pytest
36
36
37 import rhodecode
37 import rhodecode
38 from rhodecode.model.db import Repository
38 from rhodecode.model.db import Repository
39 from rhodecode.model.meta import Session
39 from rhodecode.model.meta import Session
40 from rhodecode.model.settings import SettingsModel
40 from rhodecode.model.settings import SettingsModel
41 from rhodecode.tests import (
41 from rhodecode.tests import (
42 GIT_REPO, HG_REPO, TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS,)
42 GIT_REPO, HG_REPO, TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS,)
43 from rhodecode.tests.fixture import Fixture
43 from rhodecode.tests.fixture import Fixture
44 from rhodecode.tests.utils import is_url_reachable, wait_for_url
44 from rhodecode.tests.utils import is_url_reachable, wait_for_url
45
45
46 RC_LOG = os.path.join(tempfile.gettempdir(), 'rc.log')
46 RC_LOG = os.path.join(tempfile.gettempdir(), 'rc.log')
47 REPO_GROUP = 'a_repo_group'
47 REPO_GROUP = 'a_repo_group'
48 HG_REPO_WITH_GROUP = '%s/%s' % (REPO_GROUP, HG_REPO)
48 HG_REPO_WITH_GROUP = '%s/%s' % (REPO_GROUP, HG_REPO)
49 GIT_REPO_WITH_GROUP = '%s/%s' % (REPO_GROUP, GIT_REPO)
49 GIT_REPO_WITH_GROUP = '%s/%s' % (REPO_GROUP, GIT_REPO)
50
50
51
51
52 def assert_no_running_instance(url):
52 def assert_no_running_instance(url):
53 if is_url_reachable(url):
53 if is_url_reachable(url):
54 print("Hint: Usually this means another instance of Enterprise "
54 print("Hint: Usually this means another instance of Enterprise "
55 "is running in the background.")
55 "is running in the background.")
56 pytest.fail(
56 pytest.fail(
57 "Port is not free at %s, cannot start web interface" % url)
57 "Port is not free at %s, cannot start web interface" % url)
58
58
59
59
60 def get_host_url(pylons_config):
60 def get_host_url(pyramid_config):
61 """Construct the host url using the port in the test configuration."""
61 """Construct the host url using the port in the test configuration."""
62 config = ConfigParser.ConfigParser()
62 config = ConfigParser.ConfigParser()
63 config.read(pylons_config)
63 config.read(pyramid_config)
64
64
65 return '127.0.0.1:%s' % config.get('server:main', 'port')
65 return '127.0.0.1:%s' % config.get('server:main', 'port')
66
66
67
67
68 class RcWebServer(object):
68 class RcWebServer(object):
69 """
69 """
70 Represents a running RCE web server used as a test fixture.
70 Represents a running RCE web server used as a test fixture.
71 """
71 """
72 def __init__(self, pylons_config):
72 def __init__(self, pyramid_config):
73 self.pylons_config = pylons_config
73 self.pyramid_config = pyramid_config
74
74
75 def repo_clone_url(self, repo_name, **kwargs):
75 def repo_clone_url(self, repo_name, **kwargs):
76 params = {
76 params = {
77 'user': TEST_USER_ADMIN_LOGIN,
77 'user': TEST_USER_ADMIN_LOGIN,
78 'passwd': TEST_USER_ADMIN_PASS,
78 'passwd': TEST_USER_ADMIN_PASS,
79 'host': get_host_url(self.pylons_config),
79 'host': get_host_url(self.pyramid_config),
80 'cloned_repo': repo_name,
80 'cloned_repo': repo_name,
81 }
81 }
82 params.update(**kwargs)
82 params.update(**kwargs)
83 _url = 'http://%(user)s:%(passwd)s@%(host)s/%(cloned_repo)s' % params
83 _url = 'http://%(user)s:%(passwd)s@%(host)s/%(cloned_repo)s' % params
84 return _url
84 return _url
85
85
86 def host_url(self):
86 def host_url(self):
87 return 'http://' + get_host_url(self.pylons_config)
87 return 'http://' + get_host_url(self.pyramid_config)
88
88
89
89
90 @pytest.fixture(scope="module")
90 @pytest.fixture(scope="module")
91 def rcextensions(request, baseapp, tmpdir_factory):
91 def rcextensions(request, baseapp, tmpdir_factory):
92 """
92 """
93 Installs a testing rcextensions pack to ensure they work as expected.
93 Installs a testing rcextensions pack to ensure they work as expected.
94 """
94 """
95 init_content = textwrap.dedent("""
95 init_content = textwrap.dedent("""
96 # Forward import the example rcextensions to make it
96 # Forward import the example rcextensions to make it
97 # active for our tests.
97 # active for our tests.
98 from rhodecode.tests.other.example_rcextensions import *
98 from rhodecode.tests.other.example_rcextensions import *
99 """)
99 """)
100
100
101 # Note: rcextensions are looked up based on the path of the ini file
101 # Note: rcextensions are looked up based on the path of the ini file
102 root_path = tmpdir_factory.getbasetemp()
102 root_path = tmpdir_factory.getbasetemp()
103 rcextensions_path = root_path.join('rcextensions')
103 rcextensions_path = root_path.join('rcextensions')
104 init_path = rcextensions_path.join('__init__.py')
104 init_path = rcextensions_path.join('__init__.py')
105
105
106 if rcextensions_path.check():
106 if rcextensions_path.check():
107 pytest.fail(
107 pytest.fail(
108 "Path for rcextensions already exists, please clean up before "
108 "Path for rcextensions already exists, please clean up before "
109 "test run this path: %s" % (rcextensions_path, ))
109 "test run this path: %s" % (rcextensions_path, ))
110 return
110 return
111
111
112 request.addfinalizer(rcextensions_path.remove)
112 request.addfinalizer(rcextensions_path.remove)
113 init_path.write_binary(init_content, ensure=True)
113 init_path.write_binary(init_content, ensure=True)
114
114
115
115
116 @pytest.fixture(scope="module")
116 @pytest.fixture(scope="module")
117 def repos(request, baseapp):
117 def repos(request, baseapp):
118 """Create a copy of each test repo in a repo group."""
118 """Create a copy of each test repo in a repo group."""
119 fixture = Fixture()
119 fixture = Fixture()
120 repo_group = fixture.create_repo_group(REPO_GROUP)
120 repo_group = fixture.create_repo_group(REPO_GROUP)
121 repo_group_id = repo_group.group_id
121 repo_group_id = repo_group.group_id
122 fixture.create_fork(HG_REPO, HG_REPO,
122 fixture.create_fork(HG_REPO, HG_REPO,
123 repo_name_full=HG_REPO_WITH_GROUP,
123 repo_name_full=HG_REPO_WITH_GROUP,
124 repo_group=repo_group_id)
124 repo_group=repo_group_id)
125 fixture.create_fork(GIT_REPO, GIT_REPO,
125 fixture.create_fork(GIT_REPO, GIT_REPO,
126 repo_name_full=GIT_REPO_WITH_GROUP,
126 repo_name_full=GIT_REPO_WITH_GROUP,
127 repo_group=repo_group_id)
127 repo_group=repo_group_id)
128
128
129 @request.addfinalizer
129 @request.addfinalizer
130 def cleanup():
130 def cleanup():
131 fixture.destroy_repo(HG_REPO_WITH_GROUP)
131 fixture.destroy_repo(HG_REPO_WITH_GROUP)
132 fixture.destroy_repo(GIT_REPO_WITH_GROUP)
132 fixture.destroy_repo(GIT_REPO_WITH_GROUP)
133 fixture.destroy_repo_group(repo_group_id)
133 fixture.destroy_repo_group(repo_group_id)
134
134
135
135
136 @pytest.fixture(scope="module")
136 @pytest.fixture(scope="module")
137 def rc_web_server_config(testini_factory):
137 def rc_web_server_config(testini_factory):
138 """
138 """
139 Configuration file used for the fixture `rc_web_server`.
139 Configuration file used for the fixture `rc_web_server`.
140 """
140 """
141 CUSTOM_PARAMS = [
141 CUSTOM_PARAMS = [
142 {'handler_console': {'level': 'DEBUG'}},
142 {'handler_console': {'level': 'DEBUG'}},
143 ]
143 ]
144 return testini_factory(CUSTOM_PARAMS)
144 return testini_factory(CUSTOM_PARAMS)
145
145
146
146
147 @pytest.fixture(scope="module")
147 @pytest.fixture(scope="module")
148 def rc_web_server(
148 def rc_web_server(
149 request, baseapp, rc_web_server_config, repos, rcextensions):
149 request, baseapp, rc_web_server_config, repos, rcextensions):
150 """
150 """
151 Run the web server as a subprocess.
151 Run the web server as a subprocess.
152
152
153 Since we have already a running vcsserver, this is not spawned again.
153 Since we have already a running vcsserver, this is not spawned again.
154 """
154 """
155 env = os.environ.copy()
155 env = os.environ.copy()
156 env['RC_NO_TMP_PATH'] = '1'
156 env['RC_NO_TMP_PATH'] = '1'
157
157
158 rc_log = RC_LOG
158 rc_log = RC_LOG
159 server_out = open(rc_log, 'w')
159 server_out = open(rc_log, 'w')
160
160
161 # TODO: Would be great to capture the output and err of the subprocess
161 # TODO: Would be great to capture the output and err of the subprocess
162 # and make it available in a section of the py.test report in case of an
162 # and make it available in a section of the py.test report in case of an
163 # error.
163 # error.
164
164
165 host_url = 'http://' + get_host_url(rc_web_server_config)
165 host_url = 'http://' + get_host_url(rc_web_server_config)
166 assert_no_running_instance(host_url)
166 assert_no_running_instance(host_url)
167 command = ['pserve', rc_web_server_config]
167 command = ['pserve', rc_web_server_config]
168
168
169 print('Starting rhodecode server: {}'.format(host_url))
169 print('Starting rhodecode server: {}'.format(host_url))
170 print('Command: {}'.format(command))
170 print('Command: {}'.format(command))
171 print('Logfile: {}'.format(rc_log))
171 print('Logfile: {}'.format(rc_log))
172
172
173 proc = subprocess32.Popen(
173 proc = subprocess32.Popen(
174 command, bufsize=0, env=env, stdout=server_out, stderr=server_out)
174 command, bufsize=0, env=env, stdout=server_out, stderr=server_out)
175
175
176 wait_for_url(host_url, timeout=30)
176 wait_for_url(host_url, timeout=30)
177
177
178 @request.addfinalizer
178 @request.addfinalizer
179 def stop_web_server():
179 def stop_web_server():
180 # TODO: Find out how to integrate with the reporting of py.test to
180 # TODO: Find out how to integrate with the reporting of py.test to
181 # make this information available.
181 # make this information available.
182 print("\nServer log file written to %s" % (rc_log, ))
182 print("\nServer log file written to %s" % (rc_log, ))
183 proc.kill()
183 proc.kill()
184 server_out.flush()
184 server_out.flush()
185 server_out.close()
185 server_out.close()
186
186
187 return RcWebServer(rc_web_server_config)
187 return RcWebServer(rc_web_server_config)
188
188
189
189
190 @pytest.fixture
190 @pytest.fixture
191 def disable_locking(baseapp):
191 def disable_locking(baseapp):
192 r = Repository.get_by_repo_name(GIT_REPO)
192 r = Repository.get_by_repo_name(GIT_REPO)
193 Repository.unlock(r)
193 Repository.unlock(r)
194 r.enable_locking = False
194 r.enable_locking = False
195 Session().add(r)
195 Session().add(r)
196 Session().commit()
196 Session().commit()
197
197
198 r = Repository.get_by_repo_name(HG_REPO)
198 r = Repository.get_by_repo_name(HG_REPO)
199 Repository.unlock(r)
199 Repository.unlock(r)
200 r.enable_locking = False
200 r.enable_locking = False
201 Session().add(r)
201 Session().add(r)
202 Session().commit()
202 Session().commit()
203
203
204
204
205 @pytest.fixture
205 @pytest.fixture
206 def enable_auth_plugins(request, baseapp, csrf_token):
206 def enable_auth_plugins(request, baseapp, csrf_token):
207 """
207 """
208 Return a factory object that when called, allows to control which
208 Return a factory object that when called, allows to control which
209 authentication plugins are enabled.
209 authentication plugins are enabled.
210 """
210 """
211 def _enable_plugins(plugins_list, override=None):
211 def _enable_plugins(plugins_list, override=None):
212 override = override or {}
212 override = override or {}
213 params = {
213 params = {
214 'auth_plugins': ','.join(plugins_list),
214 'auth_plugins': ','.join(plugins_list),
215 }
215 }
216
216
217 # helper translate some names to others
217 # helper translate some names to others
218 name_map = {
218 name_map = {
219 'token': 'authtoken'
219 'token': 'authtoken'
220 }
220 }
221
221
222 for module in plugins_list:
222 for module in plugins_list:
223 plugin_name = module.partition('#')[-1]
223 plugin_name = module.partition('#')[-1]
224 if plugin_name in name_map:
224 if plugin_name in name_map:
225 plugin_name = name_map[plugin_name]
225 plugin_name = name_map[plugin_name]
226 enabled_plugin = 'auth_%s_enabled' % plugin_name
226 enabled_plugin = 'auth_%s_enabled' % plugin_name
227 cache_ttl = 'auth_%s_cache_ttl' % plugin_name
227 cache_ttl = 'auth_%s_cache_ttl' % plugin_name
228
228
229 # default params that are needed for each plugin,
229 # default params that are needed for each plugin,
230 # `enabled` and `cache_ttl`
230 # `enabled` and `cache_ttl`
231 params.update({
231 params.update({
232 enabled_plugin: True,
232 enabled_plugin: True,
233 cache_ttl: 0
233 cache_ttl: 0
234 })
234 })
235 if override.get:
235 if override.get:
236 params.update(override.get(module, {}))
236 params.update(override.get(module, {}))
237
237
238 validated_params = params
238 validated_params = params
239 for k, v in validated_params.items():
239 for k, v in validated_params.items():
240 setting = SettingsModel().create_or_update_setting(k, v)
240 setting = SettingsModel().create_or_update_setting(k, v)
241 Session().add(setting)
241 Session().add(setting)
242 Session().commit()
242 Session().commit()
243
243
244 def cleanup():
244 def cleanup():
245 _enable_plugins(['egg:rhodecode-enterprise-ce#rhodecode'])
245 _enable_plugins(['egg:rhodecode-enterprise-ce#rhodecode'])
246
246
247 request.addfinalizer(cleanup)
247 request.addfinalizer(cleanup)
248
248
249 return _enable_plugins
249 return _enable_plugins
250
250
251
251
252 @pytest.fixture
252 @pytest.fixture
253 def fs_repo_only(request, rhodecode_fixtures):
253 def fs_repo_only(request, rhodecode_fixtures):
254 def fs_repo_fabric(repo_name, repo_type):
254 def fs_repo_fabric(repo_name, repo_type):
255 rhodecode_fixtures.create_repo(repo_name, repo_type=repo_type)
255 rhodecode_fixtures.create_repo(repo_name, repo_type=repo_type)
256 rhodecode_fixtures.destroy_repo(repo_name, fs_remove=False)
256 rhodecode_fixtures.destroy_repo(repo_name, fs_remove=False)
257
257
258 def cleanup():
258 def cleanup():
259 rhodecode_fixtures.destroy_repo(repo_name, fs_remove=True)
259 rhodecode_fixtures.destroy_repo(repo_name, fs_remove=True)
260 rhodecode_fixtures.destroy_repo_on_filesystem(repo_name)
260 rhodecode_fixtures.destroy_repo_on_filesystem(repo_name)
261
261
262 request.addfinalizer(cleanup)
262 request.addfinalizer(cleanup)
263
263
264 return fs_repo_fabric
264 return fs_repo_fabric
@@ -1,386 +1,386 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import os
21 import os
22 import json
22 import json
23 import platform
23 import platform
24 import socket
24 import socket
25 import subprocess32
25 import subprocess32
26 import time
26 import time
27 from urllib2 import urlopen, URLError
27 from urllib2 import urlopen, URLError
28
28
29 import configobj
29 import configobj
30 import pytest
30 import pytest
31
31
32 import pyramid.paster
32 import pyramid.paster
33 from rhodecode.tests.fixture import TestINI
33 from rhodecode.tests.fixture import TestINI
34 import rhodecode
34 import rhodecode
35
35
36
36
37 def _parse_json(value):
37 def _parse_json(value):
38 return json.loads(value) if value else None
38 return json.loads(value) if value else None
39
39
40
40
41 def pytest_addoption(parser):
41 def pytest_addoption(parser):
42 parser.addoption(
42 parser.addoption(
43 '--test-loglevel', dest='test_loglevel',
43 '--test-loglevel', dest='test_loglevel',
44 help="Set default Logging level for tests, warn (default), info, debug")
44 help="Set default Logging level for tests, warn (default), info, debug")
45 group = parser.getgroup('pylons')
45 group = parser.getgroup('pylons')
46 group.addoption(
46 group.addoption(
47 '--with-pylons', dest='pylons_config',
47 '--with-pylons', dest='pyramid_config',
48 help="Set up a Pylons environment with the specified config file.")
48 help="Set up a Pylons environment with the specified config file.")
49 group.addoption(
49 group.addoption(
50 '--ini-config-override', action='store', type=_parse_json,
50 '--ini-config-override', action='store', type=_parse_json,
51 default=None, dest='pylons_config_override', help=(
51 default=None, dest='pyramid_config_override', help=(
52 "Overrides the .ini file settings. Should be specified in JSON"
52 "Overrides the .ini file settings. Should be specified in JSON"
53 " format, e.g. '{\"section\": {\"parameter\": \"value\", ...}}'"
53 " format, e.g. '{\"section\": {\"parameter\": \"value\", ...}}'"
54 )
54 )
55 )
55 )
56 parser.addini(
56 parser.addini(
57 'pylons_config',
57 'pyramid_config',
58 "Set up a Pylons environment with the specified config file.")
58 "Set up a Pyramid environment with the specified config file.")
59
59
60 vcsgroup = parser.getgroup('vcs')
60 vcsgroup = parser.getgroup('vcs')
61 vcsgroup.addoption(
61 vcsgroup.addoption(
62 '--without-vcsserver', dest='with_vcsserver', action='store_false',
62 '--without-vcsserver', dest='with_vcsserver', action='store_false',
63 help="Do not start the VCSServer in a background process.")
63 help="Do not start the VCSServer in a background process.")
64 vcsgroup.addoption(
64 vcsgroup.addoption(
65 '--with-vcsserver-http', dest='vcsserver_config_http',
65 '--with-vcsserver-http', dest='vcsserver_config_http',
66 help="Start the HTTP VCSServer with the specified config file.")
66 help="Start the HTTP VCSServer with the specified config file.")
67 vcsgroup.addoption(
67 vcsgroup.addoption(
68 '--vcsserver-protocol', dest='vcsserver_protocol',
68 '--vcsserver-protocol', dest='vcsserver_protocol',
69 help="Start the VCSServer with HTTP protocol support.")
69 help="Start the VCSServer with HTTP protocol support.")
70 vcsgroup.addoption(
70 vcsgroup.addoption(
71 '--vcsserver-config-override', action='store', type=_parse_json,
71 '--vcsserver-config-override', action='store', type=_parse_json,
72 default=None, dest='vcsserver_config_override', help=(
72 default=None, dest='vcsserver_config_override', help=(
73 "Overrides the .ini file settings for the VCSServer. "
73 "Overrides the .ini file settings for the VCSServer. "
74 "Should be specified in JSON "
74 "Should be specified in JSON "
75 "format, e.g. '{\"section\": {\"parameter\": \"value\", ...}}'"
75 "format, e.g. '{\"section\": {\"parameter\": \"value\", ...}}'"
76 )
76 )
77 )
77 )
78 vcsgroup.addoption(
78 vcsgroup.addoption(
79 '--vcsserver-port', action='store', type=int,
79 '--vcsserver-port', action='store', type=int,
80 default=None, help=(
80 default=None, help=(
81 "Allows to set the port of the vcsserver. Useful when testing "
81 "Allows to set the port of the vcsserver. Useful when testing "
82 "against an already running server and random ports cause "
82 "against an already running server and random ports cause "
83 "trouble."))
83 "trouble."))
84 parser.addini(
84 parser.addini(
85 'vcsserver_config_http',
85 'vcsserver_config_http',
86 "Start the HTTP VCSServer with the specified config file.")
86 "Start the HTTP VCSServer with the specified config file.")
87 parser.addini(
87 parser.addini(
88 'vcsserver_protocol',
88 'vcsserver_protocol',
89 "Start the VCSServer with HTTP protocol support.")
89 "Start the VCSServer with HTTP protocol support.")
90
90
91
91
92 @pytest.fixture(scope='session')
92 @pytest.fixture(scope='session')
93 def vcsserver(request, vcsserver_port, vcsserver_factory):
93 def vcsserver(request, vcsserver_port, vcsserver_factory):
94 """
94 """
95 Session scope VCSServer.
95 Session scope VCSServer.
96
96
97 Tests wich need the VCSServer have to rely on this fixture in order
97 Tests wich need the VCSServer have to rely on this fixture in order
98 to ensure it will be running.
98 to ensure it will be running.
99
99
100 For specific needs, the fixture vcsserver_factory can be used. It allows to
100 For specific needs, the fixture vcsserver_factory can be used. It allows to
101 adjust the configuration file for the test run.
101 adjust the configuration file for the test run.
102
102
103 Command line args:
103 Command line args:
104
104
105 --without-vcsserver: Allows to switch this fixture off. You have to
105 --without-vcsserver: Allows to switch this fixture off. You have to
106 manually start the server.
106 manually start the server.
107
107
108 --vcsserver-port: Will expect the VCSServer to listen on this port.
108 --vcsserver-port: Will expect the VCSServer to listen on this port.
109 """
109 """
110
110
111 if not request.config.getoption('with_vcsserver'):
111 if not request.config.getoption('with_vcsserver'):
112 return None
112 return None
113
113
114 use_http = _use_vcs_http_server(request.config)
114 use_http = _use_vcs_http_server(request.config)
115 return vcsserver_factory(
115 return vcsserver_factory(
116 request, use_http=use_http, vcsserver_port=vcsserver_port)
116 request, use_http=use_http, vcsserver_port=vcsserver_port)
117
117
118
118
119 @pytest.fixture(scope='session')
119 @pytest.fixture(scope='session')
120 def vcsserver_factory(tmpdir_factory):
120 def vcsserver_factory(tmpdir_factory):
121 """
121 """
122 Use this if you need a running vcsserver with a special configuration.
122 Use this if you need a running vcsserver with a special configuration.
123 """
123 """
124
124
125 def factory(request, use_http=True, overrides=(), vcsserver_port=None):
125 def factory(request, use_http=True, overrides=(), vcsserver_port=None):
126
126
127 if vcsserver_port is None:
127 if vcsserver_port is None:
128 vcsserver_port = get_available_port()
128 vcsserver_port = get_available_port()
129
129
130 overrides = list(overrides)
130 overrides = list(overrides)
131 if use_http:
131 if use_http:
132 overrides.append({'server:main': {'port': vcsserver_port}})
132 overrides.append({'server:main': {'port': vcsserver_port}})
133 else:
133 else:
134 overrides.append({'DEFAULT': {'port': vcsserver_port}})
134 overrides.append({'DEFAULT': {'port': vcsserver_port}})
135
135
136 if is_cygwin():
136 if is_cygwin():
137 platform_override = {'DEFAULT': {
137 platform_override = {'DEFAULT': {
138 'beaker.cache.repo_object.type': 'nocache'}}
138 'beaker.cache.repo_object.type': 'nocache'}}
139 overrides.append(platform_override)
139 overrides.append(platform_override)
140
140
141 option_name = 'vcsserver_config_http' if use_http else ''
141 option_name = 'vcsserver_config_http' if use_http else ''
142 override_option_name = 'vcsserver_config_override'
142 override_option_name = 'vcsserver_config_override'
143 config_file = get_config(
143 config_file = get_config(
144 request.config, option_name=option_name,
144 request.config, option_name=option_name,
145 override_option_name=override_option_name, overrides=overrides,
145 override_option_name=override_option_name, overrides=overrides,
146 basetemp=tmpdir_factory.getbasetemp().strpath,
146 basetemp=tmpdir_factory.getbasetemp().strpath,
147 prefix='test_vcs_')
147 prefix='test_vcs_')
148
148
149 print("Using the VCSServer configuration:{}".format(config_file))
149 print("Using the VCSServer configuration:{}".format(config_file))
150 ServerClass = HttpVCSServer if use_http else None
150 ServerClass = HttpVCSServer if use_http else None
151 server = ServerClass(config_file)
151 server = ServerClass(config_file)
152 server.start()
152 server.start()
153
153
154 @request.addfinalizer
154 @request.addfinalizer
155 def cleanup():
155 def cleanup():
156 server.shutdown()
156 server.shutdown()
157
157
158 server.wait_until_ready()
158 server.wait_until_ready()
159 return server
159 return server
160
160
161 return factory
161 return factory
162
162
163
163
164 def is_cygwin():
164 def is_cygwin():
165 return 'cygwin' in platform.system().lower()
165 return 'cygwin' in platform.system().lower()
166
166
167
167
168 def _use_vcs_http_server(config):
168 def _use_vcs_http_server(config):
169 protocol_option = 'vcsserver_protocol'
169 protocol_option = 'vcsserver_protocol'
170 protocol = (
170 protocol = (
171 config.getoption(protocol_option) or
171 config.getoption(protocol_option) or
172 config.getini(protocol_option) or
172 config.getini(protocol_option) or
173 'http')
173 'http')
174 return protocol == 'http'
174 return protocol == 'http'
175
175
176
176
177 def _use_log_level(config):
177 def _use_log_level(config):
178 level = config.getoption('test_loglevel') or 'warn'
178 level = config.getoption('test_loglevel') or 'warn'
179 return level.upper()
179 return level.upper()
180
180
181
181
182 class VCSServer(object):
182 class VCSServer(object):
183 """
183 """
184 Represents a running VCSServer instance.
184 Represents a running VCSServer instance.
185 """
185 """
186
186
187 _args = []
187 _args = []
188
188
189 def start(self):
189 def start(self):
190 print("Starting the VCSServer: {}".format(self._args))
190 print("Starting the VCSServer: {}".format(self._args))
191 self.process = subprocess32.Popen(self._args)
191 self.process = subprocess32.Popen(self._args)
192
192
193 def wait_until_ready(self, timeout=30):
193 def wait_until_ready(self, timeout=30):
194 raise NotImplementedError()
194 raise NotImplementedError()
195
195
196 def shutdown(self):
196 def shutdown(self):
197 self.process.kill()
197 self.process.kill()
198
198
199
199
200 class HttpVCSServer(VCSServer):
200 class HttpVCSServer(VCSServer):
201 """
201 """
202 Represents a running VCSServer instance.
202 Represents a running VCSServer instance.
203 """
203 """
204 def __init__(self, config_file):
204 def __init__(self, config_file):
205 config_data = configobj.ConfigObj(config_file)
205 config_data = configobj.ConfigObj(config_file)
206 self._config = config_data['server:main']
206 self._config = config_data['server:main']
207
207
208 args = ['pserve', config_file]
208 args = ['pserve', config_file]
209 self._args = args
209 self._args = args
210
210
211 @property
211 @property
212 def http_url(self):
212 def http_url(self):
213 template = 'http://{host}:{port}/'
213 template = 'http://{host}:{port}/'
214 return template.format(**self._config)
214 return template.format(**self._config)
215
215
216 def start(self):
216 def start(self):
217 self.process = subprocess32.Popen(self._args)
217 self.process = subprocess32.Popen(self._args)
218
218
219 def wait_until_ready(self, timeout=30):
219 def wait_until_ready(self, timeout=30):
220 host = self._config['host']
220 host = self._config['host']
221 port = self._config['port']
221 port = self._config['port']
222 status_url = 'http://{host}:{port}/status'.format(host=host, port=port)
222 status_url = 'http://{host}:{port}/status'.format(host=host, port=port)
223 start = time.time()
223 start = time.time()
224
224
225 while time.time() - start < timeout:
225 while time.time() - start < timeout:
226 try:
226 try:
227 urlopen(status_url)
227 urlopen(status_url)
228 break
228 break
229 except URLError:
229 except URLError:
230 time.sleep(0.2)
230 time.sleep(0.2)
231 else:
231 else:
232 pytest.exit(
232 pytest.exit(
233 "Starting the VCSServer failed or took more than {} "
233 "Starting the VCSServer failed or took more than {} "
234 "seconds. cmd: `{}`".format(timeout, ' '.join(self._args)))
234 "seconds. cmd: `{}`".format(timeout, ' '.join(self._args)))
235
235
236 def shutdown(self):
236 def shutdown(self):
237 self.process.kill()
237 self.process.kill()
238
238
239
239
240 @pytest.fixture(scope='session')
240 @pytest.fixture(scope='session')
241 def ini_config(request, tmpdir_factory, rcserver_port, vcsserver_port):
241 def ini_config(request, tmpdir_factory, rcserver_port, vcsserver_port):
242 option_name = 'pylons_config'
242 option_name = 'pyramid_config'
243 log_level = _use_log_level(request.config)
243 log_level = _use_log_level(request.config)
244
244
245 overrides = [
245 overrides = [
246 {'server:main': {'port': rcserver_port}},
246 {'server:main': {'port': rcserver_port}},
247 {'app:main': {
247 {'app:main': {
248 'vcs.server': 'localhost:%s' % vcsserver_port,
248 'vcs.server': 'localhost:%s' % vcsserver_port,
249 # johbo: We will always start the VCSServer on our own based on the
249 # johbo: We will always start the VCSServer on our own based on the
250 # fixtures of the test cases. For the test run it must always be
250 # fixtures of the test cases. For the test run it must always be
251 # off in the INI file.
251 # off in the INI file.
252 'vcs.start_server': 'false',
252 'vcs.start_server': 'false',
253 }},
253 }},
254
254
255 {'handler_console': {
255 {'handler_console': {
256 'class ': 'StreamHandler',
256 'class ': 'StreamHandler',
257 'args ': '(sys.stderr,)',
257 'args ': '(sys.stderr,)',
258 'level': log_level,
258 'level': log_level,
259 }},
259 }},
260
260
261 ]
261 ]
262 if _use_vcs_http_server(request.config):
262 if _use_vcs_http_server(request.config):
263 overrides.append({
263 overrides.append({
264 'app:main': {
264 'app:main': {
265 'vcs.server.protocol': 'http',
265 'vcs.server.protocol': 'http',
266 'vcs.scm_app_implementation': 'http',
266 'vcs.scm_app_implementation': 'http',
267 'vcs.hooks.protocol': 'http',
267 'vcs.hooks.protocol': 'http',
268 }
268 }
269 })
269 })
270
270
271 filename = get_config(
271 filename = get_config(
272 request.config, option_name=option_name,
272 request.config, option_name=option_name,
273 override_option_name='{}_override'.format(option_name),
273 override_option_name='{}_override'.format(option_name),
274 overrides=overrides,
274 overrides=overrides,
275 basetemp=tmpdir_factory.getbasetemp().strpath,
275 basetemp=tmpdir_factory.getbasetemp().strpath,
276 prefix='test_rce_')
276 prefix='test_rce_')
277 return filename
277 return filename
278
278
279
279
280 @pytest.fixture(scope='session')
280 @pytest.fixture(scope='session')
281 def rcserver_port(request):
281 def rcserver_port(request):
282 port = get_available_port()
282 port = get_available_port()
283 print('Using rcserver port {}'.format(port))
283 print('Using rcserver port {}'.format(port))
284 return port
284 return port
285
285
286
286
287 @pytest.fixture(scope='session')
287 @pytest.fixture(scope='session')
288 def vcsserver_port(request):
288 def vcsserver_port(request):
289 port = request.config.getoption('--vcsserver-port')
289 port = request.config.getoption('--vcsserver-port')
290 if port is None:
290 if port is None:
291 port = get_available_port()
291 port = get_available_port()
292 print('Using vcsserver port {}'.format(port))
292 print('Using vcsserver port {}'.format(port))
293 return port
293 return port
294
294
295
295
296 def get_available_port():
296 def get_available_port():
297 family = socket.AF_INET
297 family = socket.AF_INET
298 socktype = socket.SOCK_STREAM
298 socktype = socket.SOCK_STREAM
299 host = '127.0.0.1'
299 host = '127.0.0.1'
300
300
301 mysocket = socket.socket(family, socktype)
301 mysocket = socket.socket(family, socktype)
302 mysocket.bind((host, 0))
302 mysocket.bind((host, 0))
303 port = mysocket.getsockname()[1]
303 port = mysocket.getsockname()[1]
304 mysocket.close()
304 mysocket.close()
305 del mysocket
305 del mysocket
306 return port
306 return port
307
307
308
308
309 @pytest.fixture(scope='session')
309 @pytest.fixture(scope='session')
310 def available_port_factory():
310 def available_port_factory():
311 """
311 """
312 Returns a callable which returns free port numbers.
312 Returns a callable which returns free port numbers.
313 """
313 """
314 return get_available_port
314 return get_available_port
315
315
316
316
317 @pytest.fixture
317 @pytest.fixture
318 def available_port(available_port_factory):
318 def available_port(available_port_factory):
319 """
319 """
320 Gives you one free port for the current test.
320 Gives you one free port for the current test.
321
321
322 Uses "available_port_factory" to retrieve the port.
322 Uses "available_port_factory" to retrieve the port.
323 """
323 """
324 return available_port_factory()
324 return available_port_factory()
325
325
326
326
327 @pytest.fixture(scope='session')
327 @pytest.fixture(scope='session')
328 def baseapp(ini_config, vcsserver, http_environ_session):
328 def baseapp(ini_config, vcsserver, http_environ_session):
329 from rhodecode.lib.pyramid_utils import get_app_config
329 from rhodecode.lib.pyramid_utils import get_app_config
330 from rhodecode.config.middleware import make_pyramid_app
330 from rhodecode.config.middleware import make_pyramid_app
331
331
332 print("Using the RhodeCode configuration:{}".format(ini_config))
332 print("Using the RhodeCode configuration:{}".format(ini_config))
333 pyramid.paster.setup_logging(ini_config)
333 pyramid.paster.setup_logging(ini_config)
334
334
335 settings = get_app_config(ini_config)
335 settings = get_app_config(ini_config)
336 app = make_pyramid_app({'__file__': ini_config}, **settings)
336 app = make_pyramid_app({'__file__': ini_config}, **settings)
337
337
338 return app
338 return app
339
339
340
340
341 @pytest.fixture(scope='session')
341 @pytest.fixture(scope='session')
342 def testini_factory(tmpdir_factory, ini_config):
342 def testini_factory(tmpdir_factory, ini_config):
343 """
343 """
344 Factory to create an INI file based on TestINI.
344 Factory to create an INI file based on TestINI.
345
345
346 It will make sure to place the INI file in the correct directory.
346 It will make sure to place the INI file in the correct directory.
347 """
347 """
348 basetemp = tmpdir_factory.getbasetemp().strpath
348 basetemp = tmpdir_factory.getbasetemp().strpath
349 return TestIniFactory(basetemp, ini_config)
349 return TestIniFactory(basetemp, ini_config)
350
350
351
351
352 class TestIniFactory(object):
352 class TestIniFactory(object):
353
353
354 def __init__(self, basetemp, template_ini):
354 def __init__(self, basetemp, template_ini):
355 self._basetemp = basetemp
355 self._basetemp = basetemp
356 self._template_ini = template_ini
356 self._template_ini = template_ini
357
357
358 def __call__(self, ini_params, new_file_prefix='test'):
358 def __call__(self, ini_params, new_file_prefix='test'):
359 ini_file = TestINI(
359 ini_file = TestINI(
360 self._template_ini, ini_params=ini_params,
360 self._template_ini, ini_params=ini_params,
361 new_file_prefix=new_file_prefix, dir=self._basetemp)
361 new_file_prefix=new_file_prefix, dir=self._basetemp)
362 result = ini_file.create()
362 result = ini_file.create()
363 return result
363 return result
364
364
365
365
366 def get_config(
366 def get_config(
367 config, option_name, override_option_name, overrides=None,
367 config, option_name, override_option_name, overrides=None,
368 basetemp=None, prefix='test'):
368 basetemp=None, prefix='test'):
369 """
369 """
370 Find a configuration file and apply overrides for the given `prefix`.
370 Find a configuration file and apply overrides for the given `prefix`.
371 """
371 """
372 config_file = (
372 config_file = (
373 config.getoption(option_name) or config.getini(option_name))
373 config.getoption(option_name) or config.getini(option_name))
374 if not config_file:
374 if not config_file:
375 pytest.exit(
375 pytest.exit(
376 "Configuration error, could not extract {}.".format(option_name))
376 "Configuration error, could not extract {}.".format(option_name))
377
377
378 overrides = overrides or []
378 overrides = overrides or []
379 config_override = config.getoption(override_option_name)
379 config_override = config.getoption(override_option_name)
380 if config_override:
380 if config_override:
381 overrides.append(config_override)
381 overrides.append(config_override)
382 temp_ini_file = TestINI(
382 temp_ini_file = TestINI(
383 config_file, ini_params=overrides, new_file_prefix=prefix,
383 config_file, ini_params=overrides, new_file_prefix=prefix,
384 dir=basetemp)
384 dir=basetemp)
385
385
386 return temp_ini_file.create()
386 return temp_ini_file.create()
General Comments 0
You need to be logged in to leave comments. Login now