##// END OF EJS Templates
test: moved lxml to local imports since this only exists during tests.
marcink -
r1239:95fd13ba default
parent child Browse files
Show More
@@ -1,297 +1,304 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 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 threading
21 import threading
22 import time
22 import time
23 import logging
23 import logging
24 import os.path
24 import os.path
25 import subprocess32
25 import subprocess32
26 import urllib2
26 import urllib2
27 from urlparse import urlparse, parse_qsl
27 from urlparse import urlparse, parse_qsl
28 from urllib import unquote_plus
28 from urllib import unquote_plus
29
29
30 import pytest
30 import pytest
31 import rc_testdata
31 import rc_testdata
32 from lxml.html import fromstring, tostring
33 from lxml.cssselect import CSSSelector
34
32
35 from rhodecode.model.db import User
33 from rhodecode.model.db import User
36 from rhodecode.model.meta import Session
34 from rhodecode.model.meta import Session
37 from rhodecode.model.scm import ScmModel
35 from rhodecode.model.scm import ScmModel
38 from rhodecode.lib.vcs.backends.svn.repository import SubversionRepository
36 from rhodecode.lib.vcs.backends.svn.repository import SubversionRepository
39
37
40
38
41 log = logging.getLogger(__name__)
39 log = logging.getLogger(__name__)
42
40
43
41
44 def set_anonymous_access(enabled):
42 def set_anonymous_access(enabled):
45 """(Dis)allows anonymous access depending on parameter `enabled`"""
43 """(Dis)allows anonymous access depending on parameter `enabled`"""
46 user = User.get_default_user()
44 user = User.get_default_user()
47 user.active = enabled
45 user.active = enabled
48 Session().add(user)
46 Session().add(user)
49 Session().commit()
47 Session().commit()
50 log.info('anonymous access is now: %s', enabled)
48 log.info('anonymous access is now: %s', enabled)
51 assert enabled == User.get_default_user().active, (
49 assert enabled == User.get_default_user().active, (
52 'Cannot set anonymous access')
50 'Cannot set anonymous access')
53
51
54
52
55 def check_xfail_backends(node, backend_alias):
53 def check_xfail_backends(node, backend_alias):
56 # Using "xfail_backends" here intentionally, since this marks work
54 # Using "xfail_backends" here intentionally, since this marks work
57 # which is "to be done" soon.
55 # which is "to be done" soon.
58 skip_marker = node.get_marker('xfail_backends')
56 skip_marker = node.get_marker('xfail_backends')
59 if skip_marker and backend_alias in skip_marker.args:
57 if skip_marker and backend_alias in skip_marker.args:
60 msg = "Support for backend %s to be developed." % (backend_alias, )
58 msg = "Support for backend %s to be developed." % (backend_alias, )
61 msg = skip_marker.kwargs.get('reason', msg)
59 msg = skip_marker.kwargs.get('reason', msg)
62 pytest.xfail(msg)
60 pytest.xfail(msg)
63
61
64
62
65 def check_skip_backends(node, backend_alias):
63 def check_skip_backends(node, backend_alias):
66 # Using "skip_backends" here intentionally, since this marks work which is
64 # Using "skip_backends" here intentionally, since this marks work which is
67 # not supported.
65 # not supported.
68 skip_marker = node.get_marker('skip_backends')
66 skip_marker = node.get_marker('skip_backends')
69 if skip_marker and backend_alias in skip_marker.args:
67 if skip_marker and backend_alias in skip_marker.args:
70 msg = "Feature not supported for backend %s." % (backend_alias, )
68 msg = "Feature not supported for backend %s." % (backend_alias, )
71 msg = skip_marker.kwargs.get('reason', msg)
69 msg = skip_marker.kwargs.get('reason', msg)
72 pytest.skip(msg)
70 pytest.skip(msg)
73
71
74
72
75 def extract_git_repo_from_dump(dump_name, repo_name):
73 def extract_git_repo_from_dump(dump_name, repo_name):
76 """Create git repo `repo_name` from dump `dump_name`."""
74 """Create git repo `repo_name` from dump `dump_name`."""
77 repos_path = ScmModel().repos_path
75 repos_path = ScmModel().repos_path
78 target_path = os.path.join(repos_path, repo_name)
76 target_path = os.path.join(repos_path, repo_name)
79 rc_testdata.extract_git_dump(dump_name, target_path)
77 rc_testdata.extract_git_dump(dump_name, target_path)
80 return target_path
78 return target_path
81
79
82
80
83 def extract_hg_repo_from_dump(dump_name, repo_name):
81 def extract_hg_repo_from_dump(dump_name, repo_name):
84 """Create hg repo `repo_name` from dump `dump_name`."""
82 """Create hg repo `repo_name` from dump `dump_name`."""
85 repos_path = ScmModel().repos_path
83 repos_path = ScmModel().repos_path
86 target_path = os.path.join(repos_path, repo_name)
84 target_path = os.path.join(repos_path, repo_name)
87 rc_testdata.extract_hg_dump(dump_name, target_path)
85 rc_testdata.extract_hg_dump(dump_name, target_path)
88 return target_path
86 return target_path
89
87
90
88
91 def extract_svn_repo_from_dump(dump_name, repo_name):
89 def extract_svn_repo_from_dump(dump_name, repo_name):
92 """Create a svn repo `repo_name` from dump `dump_name`."""
90 """Create a svn repo `repo_name` from dump `dump_name`."""
93 repos_path = ScmModel().repos_path
91 repos_path = ScmModel().repos_path
94 target_path = os.path.join(repos_path, repo_name)
92 target_path = os.path.join(repos_path, repo_name)
95 SubversionRepository(target_path, create=True)
93 SubversionRepository(target_path, create=True)
96 _load_svn_dump_into_repo(dump_name, target_path)
94 _load_svn_dump_into_repo(dump_name, target_path)
97 return target_path
95 return target_path
98
96
99
97
100 def assert_message_in_log(log_records, message, levelno, module):
98 def assert_message_in_log(log_records, message, levelno, module):
101 messages = [
99 messages = [
102 r.message for r in log_records
100 r.message for r in log_records
103 if r.module == module and r.levelno == levelno
101 if r.module == module and r.levelno == levelno
104 ]
102 ]
105 assert message in messages
103 assert message in messages
106
104
107
105
108 def _load_svn_dump_into_repo(dump_name, repo_path):
106 def _load_svn_dump_into_repo(dump_name, repo_path):
109 """
107 """
110 Utility to populate a svn repository with a named dump
108 Utility to populate a svn repository with a named dump
111
109
112 Currently the dumps are in rc_testdata. They might later on be
110 Currently the dumps are in rc_testdata. They might later on be
113 integrated with the main repository once they stabilize more.
111 integrated with the main repository once they stabilize more.
114 """
112 """
115 dump = rc_testdata.load_svn_dump(dump_name)
113 dump = rc_testdata.load_svn_dump(dump_name)
116 load_dump = subprocess32.Popen(
114 load_dump = subprocess32.Popen(
117 ['svnadmin', 'load', repo_path],
115 ['svnadmin', 'load', repo_path],
118 stdin=subprocess32.PIPE, stdout=subprocess32.PIPE,
116 stdin=subprocess32.PIPE, stdout=subprocess32.PIPE,
119 stderr=subprocess32.PIPE)
117 stderr=subprocess32.PIPE)
120 out, err = load_dump.communicate(dump)
118 out, err = load_dump.communicate(dump)
121 if load_dump.returncode != 0:
119 if load_dump.returncode != 0:
122 log.error("Output of load_dump command: %s", out)
120 log.error("Output of load_dump command: %s", out)
123 log.error("Error output of load_dump command: %s", err)
121 log.error("Error output of load_dump command: %s", err)
124 raise Exception(
122 raise Exception(
125 'Failed to load dump "%s" into repository at path "%s".'
123 'Failed to load dump "%s" into repository at path "%s".'
126 % (dump_name, repo_path))
124 % (dump_name, repo_path))
127
125
128
126
129 class AssertResponse(object):
127 class AssertResponse(object):
130 """
128 """
131 Utility that helps to assert things about a given HTML response.
129 Utility that helps to assert things about a given HTML response.
132 """
130 """
133
131
134 def __init__(self, response):
132 def __init__(self, response):
135 self.response = response
133 self.response = response
136
134
135 def get_imports(self):
136 from lxml.html import fromstring, tostring
137 from lxml.cssselect import CSSSelector
138 return fromstring, tostring, CSSSelector
139
137 def one_element_exists(self, css_selector):
140 def one_element_exists(self, css_selector):
138 self.get_element(css_selector)
141 self.get_element(css_selector)
139
142
140 def no_element_exists(self, css_selector):
143 def no_element_exists(self, css_selector):
141 assert not self._get_elements(css_selector)
144 assert not self._get_elements(css_selector)
142
145
143 def element_equals_to(self, css_selector, expected_content):
146 def element_equals_to(self, css_selector, expected_content):
144 element = self.get_element(css_selector)
147 element = self.get_element(css_selector)
145 element_text = self._element_to_string(element)
148 element_text = self._element_to_string(element)
146 assert expected_content in element_text
149 assert expected_content in element_text
147
150
148 def element_contains(self, css_selector, expected_content):
151 def element_contains(self, css_selector, expected_content):
149 element = self.get_element(css_selector)
152 element = self.get_element(css_selector)
150 assert expected_content in element.text_content()
153 assert expected_content in element.text_content()
151
154
152 def element_value_contains(self, css_selector, expected_content):
155 def element_value_contains(self, css_selector, expected_content):
153 element = self.get_element(css_selector)
156 element = self.get_element(css_selector)
154 assert expected_content in element.value
157 assert expected_content in element.value
155
158
156 def contains_one_link(self, link_text, href):
159 def contains_one_link(self, link_text, href):
160 fromstring, tostring, CSSSelector = self.get_imports()
157 doc = fromstring(self.response.body)
161 doc = fromstring(self.response.body)
158 sel = CSSSelector('a[href]')
162 sel = CSSSelector('a[href]')
159 elements = [
163 elements = [
160 e for e in sel(doc) if e.text_content().strip() == link_text]
164 e for e in sel(doc) if e.text_content().strip() == link_text]
161 assert len(elements) == 1, "Did not find link or found multiple links"
165 assert len(elements) == 1, "Did not find link or found multiple links"
162 self._ensure_url_equal(elements[0].attrib.get('href'), href)
166 self._ensure_url_equal(elements[0].attrib.get('href'), href)
163
167
164 def contains_one_anchor(self, anchor_id):
168 def contains_one_anchor(self, anchor_id):
169 fromstring, tostring, CSSSelector = self.get_imports()
165 doc = fromstring(self.response.body)
170 doc = fromstring(self.response.body)
166 sel = CSSSelector('#' + anchor_id)
171 sel = CSSSelector('#' + anchor_id)
167 elements = sel(doc)
172 elements = sel(doc)
168 assert len(elements) == 1
173 assert len(elements) == 1
169
174
170 def _ensure_url_equal(self, found, expected):
175 def _ensure_url_equal(self, found, expected):
171 assert _Url(found) == _Url(expected)
176 assert _Url(found) == _Url(expected)
172
177
173 def get_element(self, css_selector):
178 def get_element(self, css_selector):
174 elements = self._get_elements(css_selector)
179 elements = self._get_elements(css_selector)
175 assert len(elements) == 1
180 assert len(elements) == 1
176 return elements[0]
181 return elements[0]
177
182
178 def get_elements(self, css_selector):
183 def get_elements(self, css_selector):
179 return self._get_elements(css_selector)
184 return self._get_elements(css_selector)
180
185
181 def _get_elements(self, css_selector):
186 def _get_elements(self, css_selector):
187 fromstring, tostring, CSSSelector = self.get_imports()
182 doc = fromstring(self.response.body)
188 doc = fromstring(self.response.body)
183 sel = CSSSelector(css_selector)
189 sel = CSSSelector(css_selector)
184 elements = sel(doc)
190 elements = sel(doc)
185 return elements
191 return elements
186
192
187 def _element_to_string(self, element):
193 def _element_to_string(self, element):
194 fromstring, tostring, CSSSelector = self.get_imports()
188 return tostring(element)
195 return tostring(element)
189
196
190
197
191 class _Url(object):
198 class _Url(object):
192 """
199 """
193 A url object that can be compared with other url orbjects
200 A url object that can be compared with other url orbjects
194 without regard to the vagaries of encoding, escaping, and ordering
201 without regard to the vagaries of encoding, escaping, and ordering
195 of parameters in query strings.
202 of parameters in query strings.
196
203
197 Inspired by
204 Inspired by
198 http://stackoverflow.com/questions/5371992/comparing-two-urls-in-python
205 http://stackoverflow.com/questions/5371992/comparing-two-urls-in-python
199 """
206 """
200
207
201 def __init__(self, url):
208 def __init__(self, url):
202 parts = urlparse(url)
209 parts = urlparse(url)
203 _query = frozenset(parse_qsl(parts.query))
210 _query = frozenset(parse_qsl(parts.query))
204 _path = unquote_plus(parts.path)
211 _path = unquote_plus(parts.path)
205 parts = parts._replace(query=_query, path=_path)
212 parts = parts._replace(query=_query, path=_path)
206 self.parts = parts
213 self.parts = parts
207
214
208 def __eq__(self, other):
215 def __eq__(self, other):
209 return self.parts == other.parts
216 return self.parts == other.parts
210
217
211 def __hash__(self):
218 def __hash__(self):
212 return hash(self.parts)
219 return hash(self.parts)
213
220
214
221
215 def run_test_concurrently(times, raise_catched_exc=True):
222 def run_test_concurrently(times, raise_catched_exc=True):
216 """
223 """
217 Add this decorator to small pieces of code that you want to test
224 Add this decorator to small pieces of code that you want to test
218 concurrently
225 concurrently
219
226
220 ex:
227 ex:
221
228
222 @test_concurrently(25)
229 @test_concurrently(25)
223 def my_test_function():
230 def my_test_function():
224 ...
231 ...
225 """
232 """
226 def test_concurrently_decorator(test_func):
233 def test_concurrently_decorator(test_func):
227 def wrapper(*args, **kwargs):
234 def wrapper(*args, **kwargs):
228 exceptions = []
235 exceptions = []
229
236
230 def call_test_func():
237 def call_test_func():
231 try:
238 try:
232 test_func(*args, **kwargs)
239 test_func(*args, **kwargs)
233 except Exception, e:
240 except Exception as e:
234 exceptions.append(e)
241 exceptions.append(e)
235 if raise_catched_exc:
242 if raise_catched_exc:
236 raise
243 raise
237 threads = []
244 threads = []
238 for i in range(times):
245 for i in range(times):
239 threads.append(threading.Thread(target=call_test_func))
246 threads.append(threading.Thread(target=call_test_func))
240 for t in threads:
247 for t in threads:
241 t.start()
248 t.start()
242 for t in threads:
249 for t in threads:
243 t.join()
250 t.join()
244 if exceptions:
251 if exceptions:
245 raise Exception(
252 raise Exception(
246 'test_concurrently intercepted %s exceptions: %s' % (
253 'test_concurrently intercepted %s exceptions: %s' % (
247 len(exceptions), exceptions))
254 len(exceptions), exceptions))
248 return wrapper
255 return wrapper
249 return test_concurrently_decorator
256 return test_concurrently_decorator
250
257
251
258
252 def wait_for_url(url, timeout=10):
259 def wait_for_url(url, timeout=10):
253 """
260 """
254 Wait until URL becomes reachable.
261 Wait until URL becomes reachable.
255
262
256 It polls the URL until the timeout is reached or it became reachable.
263 It polls the URL until the timeout is reached or it became reachable.
257 If will call to `py.test.fail` in case the URL is not reachable.
264 If will call to `py.test.fail` in case the URL is not reachable.
258 """
265 """
259 timeout = time.time() + timeout
266 timeout = time.time() + timeout
260 last = 0
267 last = 0
261 wait = 0.1
268 wait = 0.1
262
269
263 while (timeout > last):
270 while timeout > last:
264 last = time.time()
271 last = time.time()
265 if is_url_reachable(url):
272 if is_url_reachable(url):
266 break
273 break
267 elif ((last + wait) > time.time()):
274 elif (last + wait) > time.time():
268 # Go to sleep because not enough time has passed since last check.
275 # Go to sleep because not enough time has passed since last check.
269 time.sleep(wait)
276 time.sleep(wait)
270 else:
277 else:
271 pytest.fail("Timeout while waiting for URL {}".format(url))
278 pytest.fail("Timeout while waiting for URL {}".format(url))
272
279
273
280
274 def is_url_reachable(url):
281 def is_url_reachable(url):
275 try:
282 try:
276 urllib2.urlopen(url)
283 urllib2.urlopen(url)
277 except urllib2.URLError:
284 except urllib2.URLError:
278 return False
285 return False
279 return True
286 return True
280
287
281
288
282 def get_session_from_response(response):
289 def get_session_from_response(response):
283 """
290 """
284 This returns the session from a response object. Pylons has some magic
291 This returns the session from a response object. Pylons has some magic
285 to make the session available as `response.session`. But pyramid
292 to make the session available as `response.session`. But pyramid
286 doesn't expose it.
293 doesn't expose it.
287 """
294 """
288 # TODO: Try to look up the session key also.
295 # TODO: Try to look up the session key also.
289 return response.request.environ['beaker.session']
296 return response.request.environ['beaker.session']
290
297
291
298
292 def repo_on_filesystem(repo_name):
299 def repo_on_filesystem(repo_name):
293 from rhodecode.lib import vcs
300 from rhodecode.lib import vcs
294 from rhodecode.tests import TESTS_TMP_PATH
301 from rhodecode.tests import TESTS_TMP_PATH
295 repo = vcs.get_vcs_instance(
302 repo = vcs.get_vcs_instance(
296 os.path.join(TESTS_TMP_PATH, repo_name), create=False)
303 os.path.join(TESTS_TMP_PATH, repo_name), create=False)
297 return repo is not None
304 return repo is not None
General Comments 0
You need to be logged in to leave comments. Login now