##// END OF EJS Templates
tests: increase timeouts for better test stability
marcink -
r2462:c5eece17 default
parent child Browse files
Show More
@@ -1,194 +1,194 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
22 22 import os
23 23 import time
24 24 import tempfile
25 25 import pytest
26 26 import subprocess32
27 27 import configobj
28 28
29 29 from urllib2 import urlopen, URLError
30 30 from pyramid.compat import configparser
31 31
32 32
33 33 from rhodecode.tests import TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS
34 34 from rhodecode.tests.utils import is_url_reachable
35 35
36 36
37 37 def get_port(pyramid_config):
38 38 config = configparser.ConfigParser()
39 39 config.read(pyramid_config)
40 40 return config.get('server:main', 'port')
41 41
42 42
43 43 def get_host_url(pyramid_config):
44 44 """Construct the host url using the port in the test configuration."""
45 45 return '127.0.0.1:%s' % get_port(pyramid_config)
46 46
47 47
48 48 def assert_no_running_instance(url):
49 49 if is_url_reachable(url):
50 50 print("Hint: Usually this means another instance of server "
51 51 "is running in the background at %s." % url)
52 52 pytest.fail(
53 53 "Port is not free at %s, cannot start server at" % url)
54 54
55 55
56 56 class ServerBase(object):
57 57 _args = []
58 58 log_file_name = 'NOT_DEFINED.log'
59 59 status_url_tmpl = 'http://{host}:{port}'
60 60
61 61 def __init__(self, config_file, log_file):
62 62 self.config_file = config_file
63 63 config_data = configobj.ConfigObj(config_file)
64 64 self._config = config_data['server:main']
65 65
66 66 self._args = []
67 67 self.log_file = log_file or os.path.join(
68 68 tempfile.gettempdir(), self.log_file_name)
69 69 self.process = None
70 70 self.server_out = None
71 71 print("Using the {} configuration:{}".format(
72 72 self.__class__.__name__, config_file))
73 73
74 74 if not os.path.isfile(config_file):
75 75 raise RuntimeError('Failed to get config at {}'.format(config_file))
76 76
77 77 @property
78 78 def command(self):
79 79 return ' '.join(self._args)
80 80
81 81 @property
82 82 def http_url(self):
83 83 template = 'http://{host}:{port}/'
84 84 return template.format(**self._config)
85 85
86 86 def host_url(self):
87 87 return 'http://' + get_host_url(self.config_file)
88 88
89 89 def get_rc_log(self):
90 90 with open(self.log_file) as f:
91 91 return f.read()
92 92
93 def wait_until_ready(self, timeout=15):
93 def wait_until_ready(self, timeout=30):
94 94 host = self._config['host']
95 95 port = self._config['port']
96 96 status_url = self.status_url_tmpl.format(host=host, port=port)
97 97 start = time.time()
98 98
99 99 while time.time() - start < timeout:
100 100 try:
101 101 urlopen(status_url)
102 102 break
103 103 except URLError:
104 104 time.sleep(0.2)
105 105 else:
106 106 pytest.fail(
107 107 "Starting the {} failed or took more than {} "
108 108 "seconds. cmd: `{}`".format(
109 109 self.__class__.__name__, timeout, self.command))
110 110
111 111 def shutdown(self):
112 112 self.process.kill()
113 113 self.server_out.flush()
114 114 self.server_out.close()
115 115
116 116 def get_log_file_with_port(self):
117 117 log_file = list(self.log_file.partition('.log'))
118 118 log_file.insert(1, get_port(self.config_file))
119 119 log_file = ''.join(log_file)
120 120 return log_file
121 121
122 122
123 123 class RcVCSServer(ServerBase):
124 124 """
125 125 Represents a running VCSServer instance.
126 126 """
127 127
128 128 log_file_name = 'rc-vcsserver.log'
129 129 status_url_tmpl = 'http://{host}:{port}/status'
130 130
131 131 def __init__(self, config_file, log_file=None):
132 132 super(RcVCSServer, self).__init__(config_file, log_file)
133 133 self._args = [
134 134 'gunicorn', '--paste', self.config_file]
135 135
136 136 def start(self):
137 137 env = os.environ.copy()
138 138
139 139 self.log_file = self.get_log_file_with_port()
140 140 self.server_out = open(self.log_file, 'w')
141 141
142 142 host_url = self.host_url()
143 143 assert_no_running_instance(host_url)
144 144
145 145 print('rhodecode-vcsserver starting at: {}'.format(host_url))
146 146 print('rhodecode-vcsserver command: {}'.format(self.command))
147 147 print('rhodecode-vcsserver logfile: {}'.format(self.log_file))
148 148
149 149 self.process = subprocess32.Popen(
150 150 self._args, bufsize=0, env=env,
151 151 stdout=self.server_out, stderr=self.server_out)
152 152
153 153
154 154 class RcWebServer(ServerBase):
155 155 """
156 156 Represents a running RCE web server used as a test fixture.
157 157 """
158 158
159 159 log_file_name = 'rc-web.log'
160 160 status_url_tmpl = 'http://{host}:{port}/_admin/ops/ping'
161 161
162 162 def __init__(self, config_file, log_file=None):
163 163 super(RcWebServer, self).__init__(config_file, log_file)
164 164 self._args = [
165 165 'gunicorn', '--worker-class', 'gevent', '--paste', config_file]
166 166
167 167 def start(self):
168 168 env = os.environ.copy()
169 169 env['RC_NO_TMP_PATH'] = '1'
170 170
171 171 self.log_file = self.get_log_file_with_port()
172 172 self.server_out = open(self.log_file, 'w')
173 173
174 174 host_url = self.host_url()
175 175 assert_no_running_instance(host_url)
176 176
177 177 print('rhodecode-web starting at: {}'.format(host_url))
178 178 print('rhodecode-web command: {}'.format(self.command))
179 179 print('rhodecode-web logfile: {}'.format(self.log_file))
180 180
181 181 self.process = subprocess32.Popen(
182 182 self._args, bufsize=0, env=env,
183 183 stdout=self.server_out, stderr=self.server_out)
184 184
185 185 def repo_clone_url(self, repo_name, **kwargs):
186 186 params = {
187 187 'user': TEST_USER_ADMIN_LOGIN,
188 188 'passwd': TEST_USER_ADMIN_PASS,
189 189 'host': get_host_url(self.config_file),
190 190 'cloned_repo': repo_name,
191 191 }
192 192 params.update(**kwargs)
193 193 _url = 'http://%(user)s:%(passwd)s@%(host)s/%(cloned_repo)s' % params
194 194 return _url
@@ -1,262 +1,265 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 """
22 22 py.test config for test suite for making push/pull operations.
23 23
24 24 .. important::
25 25
26 26 You must have git >= 1.8.5 for tests to work fine. With 68b939b git started
27 27 to redirect things to stderr instead of stdout.
28 28 """
29 29
30 30 import os
31 31 import tempfile
32 32 import textwrap
33 33 import pytest
34 34
35 35 from rhodecode import events
36 36 from rhodecode.model.db import Integration
37 37 from rhodecode.model.integration import IntegrationModel
38 38 from rhodecode.model.db import Repository
39 39 from rhodecode.model.meta import Session
40 40 from rhodecode.model.settings import SettingsModel
41 41 from rhodecode.integrations.types.webhook import WebhookIntegrationType
42 42
43 43 from rhodecode.tests import GIT_REPO, HG_REPO
44 44 from rhodecode.tests.fixture import Fixture
45 45 from rhodecode.tests.server_utils import RcWebServer
46 46
47 47 REPO_GROUP = 'a_repo_group'
48 48 HG_REPO_WITH_GROUP = '%s/%s' % (REPO_GROUP, HG_REPO)
49 49 GIT_REPO_WITH_GROUP = '%s/%s' % (REPO_GROUP, GIT_REPO)
50 50
51 51
52 52 @pytest.fixture(scope="module")
53 53 def rcextensions(request, db_connection, tmpdir_factory):
54 54 """
55 55 Installs a testing rcextensions pack to ensure they work as expected.
56 56 """
57 57 init_content = textwrap.dedent("""
58 58 # Forward import the example rcextensions to make it
59 59 # active for our tests.
60 60 from rhodecode.tests.other.example_rcextensions import *
61 61 """)
62 62
63 63 # Note: rcextensions are looked up based on the path of the ini file
64 64 root_path = tmpdir_factory.getbasetemp()
65 65 rcextensions_path = root_path.join('rcextensions')
66 66 init_path = rcextensions_path.join('__init__.py')
67 67
68 68 if rcextensions_path.check():
69 69 pytest.fail(
70 70 "Path for rcextensions already exists, please clean up before "
71 71 "test run this path: %s" % (rcextensions_path, ))
72 72 return
73 73
74 74 request.addfinalizer(rcextensions_path.remove)
75 75 init_path.write_binary(init_content, ensure=True)
76 76
77 77
78 78 @pytest.fixture(scope="module")
79 79 def repos(request, db_connection):
80 80 """Create a copy of each test repo in a repo group."""
81 81 fixture = Fixture()
82 82 repo_group = fixture.create_repo_group(REPO_GROUP)
83 83 repo_group_id = repo_group.group_id
84 84 fixture.create_fork(HG_REPO, HG_REPO,
85 85 repo_name_full=HG_REPO_WITH_GROUP,
86 86 repo_group=repo_group_id)
87 87 fixture.create_fork(GIT_REPO, GIT_REPO,
88 88 repo_name_full=GIT_REPO_WITH_GROUP,
89 89 repo_group=repo_group_id)
90 90
91 91 @request.addfinalizer
92 92 def cleanup():
93 93 fixture.destroy_repo(HG_REPO_WITH_GROUP)
94 94 fixture.destroy_repo(GIT_REPO_WITH_GROUP)
95 95 fixture.destroy_repo_group(repo_group_id)
96 96
97 97
98 98 @pytest.fixture(scope="module")
99 99 def rc_web_server_config_modification():
100 100 return []
101 101
102 102
103 103 @pytest.fixture(scope="module")
104 104 def rc_web_server_config_factory(testini_factory, rc_web_server_config_modification):
105 105 """
106 106 Configuration file used for the fixture `rc_web_server`.
107 107 """
108 108
109 109 def factory(vcsserver_port):
110 110 custom_params = [
111 111 {'handler_console': {'level': 'DEBUG'}},
112 112 {'app:main': {'vcs.server': 'localhost:%s' % vcsserver_port}}
113 113 ]
114 114 custom_params.extend(rc_web_server_config_modification)
115 115 return testini_factory(custom_params)
116 116 return factory
117 117
118 118
119 119 @pytest.fixture(scope="module")
120 120 def rc_web_server(
121 121 request, vcsserver_factory, available_port_factory,
122 122 rc_web_server_config_factory, repos, rcextensions):
123 123 """
124 124 Run the web server as a subprocess. with it's own instance of vcsserver
125 125 """
126 126
127 127 vcsserver_port = available_port_factory()
128 128 print('Using vcsserver ops test port {}'.format(vcsserver_port))
129 129
130 130 vcs_log = os.path.join(tempfile.gettempdir(), 'rc_op_vcs.log')
131 131 vcsserver_factory(
132 132 request, vcsserver_port=vcsserver_port,
133 133 log_file=vcs_log,
134 overrides=({'server:main': {'workers': 2}},))
134 overrides=(
135 {'server:main': {'workers': 2}},
136 {'server:main': {'graceful_timeout': 10}},
137 ))
135 138
136 139 rc_log = os.path.join(tempfile.gettempdir(), 'rc_op_web.log')
137 140 rc_web_server_config = rc_web_server_config_factory(
138 141 vcsserver_port=vcsserver_port)
139 142 server = RcWebServer(rc_web_server_config, log_file=rc_log)
140 143 server.start()
141 144
142 145 @request.addfinalizer
143 146 def cleanup():
144 147 server.shutdown()
145 148
146 149 server.wait_until_ready()
147 150 return server
148 151
149 152
150 153 @pytest.fixture
151 154 def disable_locking(baseapp):
152 155 r = Repository.get_by_repo_name(GIT_REPO)
153 156 Repository.unlock(r)
154 157 r.enable_locking = False
155 158 Session().add(r)
156 159 Session().commit()
157 160
158 161 r = Repository.get_by_repo_name(HG_REPO)
159 162 Repository.unlock(r)
160 163 r.enable_locking = False
161 164 Session().add(r)
162 165 Session().commit()
163 166
164 167
165 168 @pytest.fixture
166 169 def enable_auth_plugins(request, baseapp, csrf_token):
167 170 """
168 171 Return a factory object that when called, allows to control which
169 172 authentication plugins are enabled.
170 173 """
171 174 def _enable_plugins(plugins_list, override=None):
172 175 override = override or {}
173 176 params = {
174 177 'auth_plugins': ','.join(plugins_list),
175 178 }
176 179
177 180 # helper translate some names to others
178 181 name_map = {
179 182 'token': 'authtoken'
180 183 }
181 184
182 185 for module in plugins_list:
183 186 plugin_name = module.partition('#')[-1]
184 187 if plugin_name in name_map:
185 188 plugin_name = name_map[plugin_name]
186 189 enabled_plugin = 'auth_%s_enabled' % plugin_name
187 190 cache_ttl = 'auth_%s_cache_ttl' % plugin_name
188 191
189 192 # default params that are needed for each plugin,
190 193 # `enabled` and `cache_ttl`
191 194 params.update({
192 195 enabled_plugin: True,
193 196 cache_ttl: 0
194 197 })
195 198 if override.get:
196 199 params.update(override.get(module, {}))
197 200
198 201 validated_params = params
199 202 for k, v in validated_params.items():
200 203 setting = SettingsModel().create_or_update_setting(k, v)
201 204 Session().add(setting)
202 205 Session().commit()
203 206
204 207 def cleanup():
205 208 _enable_plugins(['egg:rhodecode-enterprise-ce#rhodecode'])
206 209
207 210 request.addfinalizer(cleanup)
208 211
209 212 return _enable_plugins
210 213
211 214
212 215 @pytest.fixture
213 216 def fs_repo_only(request, rhodecode_fixtures):
214 217 def fs_repo_fabric(repo_name, repo_type):
215 218 rhodecode_fixtures.create_repo(repo_name, repo_type=repo_type)
216 219 rhodecode_fixtures.destroy_repo(repo_name, fs_remove=False)
217 220
218 221 def cleanup():
219 222 rhodecode_fixtures.destroy_repo(repo_name, fs_remove=True)
220 223 rhodecode_fixtures.destroy_repo_on_filesystem(repo_name)
221 224
222 225 request.addfinalizer(cleanup)
223 226
224 227 return fs_repo_fabric
225 228
226 229
227 230 @pytest.fixture
228 231 def enable_webhook_push_integration(request):
229 232 integration = Integration()
230 233 integration.integration_type = WebhookIntegrationType.key
231 234 Session().add(integration)
232 235
233 236 settings = dict(
234 237 url='http://httpbin.org',
235 238 secret_token='secret',
236 239 username=None,
237 240 password=None,
238 241 custom_header_key=None,
239 242 custom_header_val=None,
240 243 method_type='get',
241 244 events=[events.RepoPushEvent.name],
242 245 log_data=True
243 246 )
244 247
245 248 IntegrationModel().update_integration(
246 249 integration,
247 250 name='IntegrationWebhookTest',
248 251 enabled=True,
249 252 settings=settings,
250 253 repo=None,
251 254 repo_group=None,
252 255 child_repos_only=False,
253 256 )
254 257 Session().commit()
255 258 integration_id = integration.integration_id
256 259
257 260 @request.addfinalizer
258 261 def cleanup():
259 262 integration = Integration.get(integration_id)
260 263 Session().delete(integration)
261 264 Session().commit()
262 265
General Comments 0
You need to be logged in to leave comments. Login now