##// END OF EJS Templates
tests: use pytest fail when vcs_operations cannot start required components
marcink -
r2461:e0c700db default
parent child Browse files
Show More
@@ -1,194 +1,194 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 import os
22 import os
23 import time
23 import time
24 import tempfile
24 import tempfile
25 import pytest
25 import pytest
26 import subprocess32
26 import subprocess32
27 import configobj
27 import configobj
28
28
29 from urllib2 import urlopen, URLError
29 from urllib2 import urlopen, URLError
30 from pyramid.compat import configparser
30 from pyramid.compat import configparser
31
31
32
32
33 from rhodecode.tests import TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS
33 from rhodecode.tests import TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS
34 from rhodecode.tests.utils import is_url_reachable
34 from rhodecode.tests.utils import is_url_reachable
35
35
36
36
37 def get_port(pyramid_config):
37 def get_port(pyramid_config):
38 config = configparser.ConfigParser()
38 config = configparser.ConfigParser()
39 config.read(pyramid_config)
39 config.read(pyramid_config)
40 return config.get('server:main', 'port')
40 return config.get('server:main', 'port')
41
41
42
42
43 def get_host_url(pyramid_config):
43 def get_host_url(pyramid_config):
44 """Construct the host url using the port in the test configuration."""
44 """Construct the host url using the port in the test configuration."""
45 return '127.0.0.1:%s' % get_port(pyramid_config)
45 return '127.0.0.1:%s' % get_port(pyramid_config)
46
46
47
47
48 def assert_no_running_instance(url):
48 def assert_no_running_instance(url):
49 if is_url_reachable(url):
49 if is_url_reachable(url):
50 print("Hint: Usually this means another instance of server "
50 print("Hint: Usually this means another instance of server "
51 "is running in the background at %s." % url)
51 "is running in the background at %s." % url)
52 pytest.fail(
52 pytest.fail(
53 "Port is not free at %s, cannot start server at" % url)
53 "Port is not free at %s, cannot start server at" % url)
54
54
55
55
56 class ServerBase(object):
56 class ServerBase(object):
57 _args = []
57 _args = []
58 log_file_name = 'NOT_DEFINED.log'
58 log_file_name = 'NOT_DEFINED.log'
59 status_url_tmpl = 'http://{host}:{port}'
59 status_url_tmpl = 'http://{host}:{port}'
60
60
61 def __init__(self, config_file, log_file):
61 def __init__(self, config_file, log_file):
62 self.config_file = config_file
62 self.config_file = config_file
63 config_data = configobj.ConfigObj(config_file)
63 config_data = configobj.ConfigObj(config_file)
64 self._config = config_data['server:main']
64 self._config = config_data['server:main']
65
65
66 self._args = []
66 self._args = []
67 self.log_file = log_file or os.path.join(
67 self.log_file = log_file or os.path.join(
68 tempfile.gettempdir(), self.log_file_name)
68 tempfile.gettempdir(), self.log_file_name)
69 self.process = None
69 self.process = None
70 self.server_out = None
70 self.server_out = None
71 print("Using the {} configuration:{}".format(
71 print("Using the {} configuration:{}".format(
72 self.__class__.__name__, config_file))
72 self.__class__.__name__, config_file))
73
73
74 if not os.path.isfile(config_file):
74 if not os.path.isfile(config_file):
75 raise RuntimeError('Failed to get config at {}'.format(config_file))
75 raise RuntimeError('Failed to get config at {}'.format(config_file))
76
76
77 @property
77 @property
78 def command(self):
78 def command(self):
79 return ' '.join(self._args)
79 return ' '.join(self._args)
80
80
81 @property
81 @property
82 def http_url(self):
82 def http_url(self):
83 template = 'http://{host}:{port}/'
83 template = 'http://{host}:{port}/'
84 return template.format(**self._config)
84 return template.format(**self._config)
85
85
86 def host_url(self):
86 def host_url(self):
87 return 'http://' + get_host_url(self.config_file)
87 return 'http://' + get_host_url(self.config_file)
88
88
89 def get_rc_log(self):
89 def get_rc_log(self):
90 with open(self.log_file) as f:
90 with open(self.log_file) as f:
91 return f.read()
91 return f.read()
92
92
93 def wait_until_ready(self, timeout=15):
93 def wait_until_ready(self, timeout=15):
94 host = self._config['host']
94 host = self._config['host']
95 port = self._config['port']
95 port = self._config['port']
96 status_url = self.status_url_tmpl.format(host=host, port=port)
96 status_url = self.status_url_tmpl.format(host=host, port=port)
97 start = time.time()
97 start = time.time()
98
98
99 while time.time() - start < timeout:
99 while time.time() - start < timeout:
100 try:
100 try:
101 urlopen(status_url)
101 urlopen(status_url)
102 break
102 break
103 except URLError:
103 except URLError:
104 time.sleep(0.2)
104 time.sleep(0.2)
105 else:
105 else:
106 pytest.exit(
106 pytest.fail(
107 "Starting the {} failed or took more than {} "
107 "Starting the {} failed or took more than {} "
108 "seconds. cmd: `{}`".format(
108 "seconds. cmd: `{}`".format(
109 self.__class__.__name__, timeout, self.command))
109 self.__class__.__name__, timeout, self.command))
110
110
111 def shutdown(self):
111 def shutdown(self):
112 self.process.kill()
112 self.process.kill()
113 self.server_out.flush()
113 self.server_out.flush()
114 self.server_out.close()
114 self.server_out.close()
115
115
116 def get_log_file_with_port(self):
116 def get_log_file_with_port(self):
117 log_file = list(self.log_file.partition('.log'))
117 log_file = list(self.log_file.partition('.log'))
118 log_file.insert(1, get_port(self.config_file))
118 log_file.insert(1, get_port(self.config_file))
119 log_file = ''.join(log_file)
119 log_file = ''.join(log_file)
120 return log_file
120 return log_file
121
121
122
122
123 class RcVCSServer(ServerBase):
123 class RcVCSServer(ServerBase):
124 """
124 """
125 Represents a running VCSServer instance.
125 Represents a running VCSServer instance.
126 """
126 """
127
127
128 log_file_name = 'rc-vcsserver.log'
128 log_file_name = 'rc-vcsserver.log'
129 status_url_tmpl = 'http://{host}:{port}/status'
129 status_url_tmpl = 'http://{host}:{port}/status'
130
130
131 def __init__(self, config_file, log_file=None):
131 def __init__(self, config_file, log_file=None):
132 super(RcVCSServer, self).__init__(config_file, log_file)
132 super(RcVCSServer, self).__init__(config_file, log_file)
133 self._args = [
133 self._args = [
134 'gunicorn', '--paste', self.config_file]
134 'gunicorn', '--paste', self.config_file]
135
135
136 def start(self):
136 def start(self):
137 env = os.environ.copy()
137 env = os.environ.copy()
138
138
139 self.log_file = self.get_log_file_with_port()
139 self.log_file = self.get_log_file_with_port()
140 self.server_out = open(self.log_file, 'w')
140 self.server_out = open(self.log_file, 'w')
141
141
142 host_url = self.host_url()
142 host_url = self.host_url()
143 assert_no_running_instance(host_url)
143 assert_no_running_instance(host_url)
144
144
145 print('rhodecode-vcsserver starting at: {}'.format(host_url))
145 print('rhodecode-vcsserver starting at: {}'.format(host_url))
146 print('rhodecode-vcsserver command: {}'.format(self.command))
146 print('rhodecode-vcsserver command: {}'.format(self.command))
147 print('rhodecode-vcsserver logfile: {}'.format(self.log_file))
147 print('rhodecode-vcsserver logfile: {}'.format(self.log_file))
148
148
149 self.process = subprocess32.Popen(
149 self.process = subprocess32.Popen(
150 self._args, bufsize=0, env=env,
150 self._args, bufsize=0, env=env,
151 stdout=self.server_out, stderr=self.server_out)
151 stdout=self.server_out, stderr=self.server_out)
152
152
153
153
154 class RcWebServer(ServerBase):
154 class RcWebServer(ServerBase):
155 """
155 """
156 Represents a running RCE web server used as a test fixture.
156 Represents a running RCE web server used as a test fixture.
157 """
157 """
158
158
159 log_file_name = 'rc-web.log'
159 log_file_name = 'rc-web.log'
160 status_url_tmpl = 'http://{host}:{port}/_admin/ops/ping'
160 status_url_tmpl = 'http://{host}:{port}/_admin/ops/ping'
161
161
162 def __init__(self, config_file, log_file=None):
162 def __init__(self, config_file, log_file=None):
163 super(RcWebServer, self).__init__(config_file, log_file)
163 super(RcWebServer, self).__init__(config_file, log_file)
164 self._args = [
164 self._args = [
165 'gunicorn', '--worker-class', 'gevent', '--paste', config_file]
165 'gunicorn', '--worker-class', 'gevent', '--paste', config_file]
166
166
167 def start(self):
167 def start(self):
168 env = os.environ.copy()
168 env = os.environ.copy()
169 env['RC_NO_TMP_PATH'] = '1'
169 env['RC_NO_TMP_PATH'] = '1'
170
170
171 self.log_file = self.get_log_file_with_port()
171 self.log_file = self.get_log_file_with_port()
172 self.server_out = open(self.log_file, 'w')
172 self.server_out = open(self.log_file, 'w')
173
173
174 host_url = self.host_url()
174 host_url = self.host_url()
175 assert_no_running_instance(host_url)
175 assert_no_running_instance(host_url)
176
176
177 print('rhodecode-web starting at: {}'.format(host_url))
177 print('rhodecode-web starting at: {}'.format(host_url))
178 print('rhodecode-web command: {}'.format(self.command))
178 print('rhodecode-web command: {}'.format(self.command))
179 print('rhodecode-web logfile: {}'.format(self.log_file))
179 print('rhodecode-web logfile: {}'.format(self.log_file))
180
180
181 self.process = subprocess32.Popen(
181 self.process = subprocess32.Popen(
182 self._args, bufsize=0, env=env,
182 self._args, bufsize=0, env=env,
183 stdout=self.server_out, stderr=self.server_out)
183 stdout=self.server_out, stderr=self.server_out)
184
184
185 def repo_clone_url(self, repo_name, **kwargs):
185 def repo_clone_url(self, repo_name, **kwargs):
186 params = {
186 params = {
187 'user': TEST_USER_ADMIN_LOGIN,
187 'user': TEST_USER_ADMIN_LOGIN,
188 'passwd': TEST_USER_ADMIN_PASS,
188 'passwd': TEST_USER_ADMIN_PASS,
189 'host': get_host_url(self.config_file),
189 'host': get_host_url(self.config_file),
190 'cloned_repo': repo_name,
190 'cloned_repo': repo_name,
191 }
191 }
192 params.update(**kwargs)
192 params.update(**kwargs)
193 _url = 'http://%(user)s:%(passwd)s@%(host)s/%(cloned_repo)s' % params
193 _url = 'http://%(user)s:%(passwd)s@%(host)s/%(cloned_repo)s' % params
194 return _url
194 return _url
General Comments 0
You need to be logged in to leave comments. Login now